Hack and Play

プログラミングやCG、ゲーム、コンピュータのネタを投稿していくブログ。不定期更新。

表情ジェネレータについて(使い方編)

2014年01月11日 | CG関連

先日じゃんけんゲーム http://www.unitygames.jp/game/ug7000830 を作った際に、表情をつけるのが大変だなーと思い、ふと

自動で表情生成できたらおもしろんじゃね?

という欲が出て、作ることを計画しました。ということで、できました。

できたものは、

 http://www.unitygames.jp/game/ug7000851

にあります。
この記事では使い方について解説をしたいと思います


1.初期画面について

まず、作りたい表情のキーワードを入力します。あらかじめ入れられているキーワードは「かわいい」、「なごむ」、「悲しい」、「怒ってる」が入っていますが、キーボード入力から変更が可能です。好きな表情のキーワードを入れてください。
「表情パラメータ」変更のチェックボックスについては、記事の最後に解説します

入れ終わったら、次へを押します。

 

2.表情のキーワードごとの評価と表情の再生成


 
次の画面では、いろいろメニューが出てきます。「次の表情」、「前の表情」ボタンで生成された表情を行き来できます。その右側の「再生成」ボタンは表情を作り直すことができます。
画面中央の「パラメータ表示」のチェックボックスを押すと、作られた表情の各モーフのウェイト値(0から1の範囲)を見ることができます。
キーワードの右側のスライドを動かして、この表情がキーワードにあっているかを評価します。度合いで示し、右側(プラス側)に行くほど、その表情はキーワードとマッチしている、左側(マイナス側)はぜんぜんあっていない場合になり、中央値(0付近)はどちらとも取れる、もしくは分からないときにここにあわせます。
続けて「次の表情」を押して評価をしていきます。
 
また、「3.すべて評価したら・・・」の下に「計算!」、「表情削除」「表情追加」ボタンがあります。
「表情追加」ボタンを押すと、現在の表情数の上限が5増え、新たに表情が追加されます。
「表情削除」ボタンを押すと、表情数が5減りますが、初期の表情数よりも少なくなることはありません。
 
 
3.すべての表情が評価終わったら

そして、「計算!」ボタンを押すことにより、次の画面に映ります。
この画面では、キーワードごとの評価結果から推定した表情を生成します。
各キーワードのボタンを押して、スライダを動かすと、表情が変わります。
その下のテキストボックスは各モーフのウェイト値を参照しています。
評価した表情が少ないと、値が収束されず、変な表情になりやすいと思います。
その場合は「前に戻る」ボタンをおして、前の画面に戻り、表情数を増やして評価し、再度計算しなおしてみてください。
表情数を増やして評価をすると、データが収束し、

 
表情もそれっぽいのができあがります。
他にも、

 
といった表情を、各キーワードに対して評価をしておくと、生成することができます。
いろいろ試してみてください!
 
 
4.自分で変化させるモーフを指定する。

最初の画面で、「表情パラメータ変更」のチェックボックスがあったと思いますが、このチェックボックスをオンにして、テキストボックス内の設定を書き換えることで、自分でモーフの設定を変更することができます。
※入力時はテキストフィールドで改行が反映されないので、いったんメモ帳などからコピペするのをお勧めします。
 
 
実際のパラメータを抜粋すると・・・
 
0-1: 困る;
0-1: にこり;
0-1: 怒り;
・・・
 
という風になっていると思います。
これは以下のような構成になっています。
[モーフのウェイト値設定]: [モーフ名];
[モーフのウェイト値設定]: [モーフ名1], [モーフ名2];
 
という風になっており、コロン(:)とセミコロン(;)で識別しています。これを忘れると正常に認識できないので注意してください。また、パラメータによっては複数モーフをしていする場合があり、その場合はカンマ(,)で区切ります。
モーフのウェイト値設定のタイプは合わせて7種類あり、以下のようになります
 
  • "*0,5"など  =  モーフを固定数(0.5)のにする
  • 0-1または0.1-1.0  =  モーフのウェイトを0~1のランダムな数に設定する
  • "0.1-0.8"など  =  モーフのウェイトを前の数字(0.1)から後ろの数字(0.8)までの間で生成する
  • a-b  =  ランダムな数字r(0~1)に対し、前のモーフはrの値、後ろのモーフは(1-r)の値をウェイトに設定する。(書き方は、a-b: [モーフ1], [モーフ2]; となる) 
  • a-0-b  =  ランダムな数字r(-1~1)に対し一方のモーフは+側の数値、一方のモーフは-側の数値を参照する。(一方の値が割り振られたら、もう一方は常に0になる)
  • "[0.2-0.7]"など  =  0~1のランダムな数を生成し、前の数字未満(0.2)になった場合は、すべて0になる、また後ろの数字(0.7)よりも大きい場合はすべて1になる
  • a^b  =  モーフ間のウェイトの合計値を1未満にする。(計算式は[各モーフのランダム値]/[モーフのランダム値の合計]x[0~1のランダムな数])、また、2つ以上のモーフを使うことができ、3つの場合の記述は"a^b^c: [モーフ1], [モーフ2], [モーフ3];"、4つの場合は"a^b^c^d: [モーフ1], [モーフ2], [モーフ3], [モーフ4];"となる
使用できるモーフ名は
 
眉系: 真面目、困る、にこり、怒り、上、下、前、眉頭左、眉頭右
 
目系:まばたき、笑い、ウィンク、ウィンク右、ウィンク2、ウィンク2右、なごみ、はぅ、びっくり、じとめ、キリッ、(はちゅ目、はちゅ目縦潰れ、はちゅ目横潰れ、)、星目、はぁと、瞳小、瞳縦潰れ、光下、(恐ろしい子!)、ハイライト消し、映り込み消
 
口系:あ、い、う、え、お、あ2、ん、(▲)、^、(□、ワ)、ω、(ω□)、にやり、にやり2、にっこり、ぺろっ、てへぺろ、てへぺろ2、口角上げ、口角下げ、口横広げ、歯無し上、歯無し下
 
他:(涙)、輪郭、照れ、がーん、みっぱい、(メガネ)、(LightUp、LightOff、LightBlink、LightBS)、青ざめる、(髪影消、照れ消、AL未使用)
 
などがあります。カッコつきのモーフに関しては、差し替え系や効果が分からないものなので注意が必要です。
たとえば、はちゅ目にしたい場合は、モーフのウェイトは0か1にしたいので、"[0.4999-0.5001]: はちゅ目;" とすればできますが、他の目系にウェイトをかけてると、へんな線が出たりするので、取り扱いが難かしいと思います。
 
また、口系や目系は複数のウェイトをかけると、顔面が崩れやすいので、a^bのパラメータをかけるか、0.0-0.5など、最大値を抑えるなどの工夫がいるかと思います。
 

UnityのPlaymakerとC#による状態変移の制御について

2014年01月03日 | CG関連

UnityのAssetStoreで販売している、Playmakerを購入しました。

http://www.hutonggames.com/index.html

このPlaymakerは、スクリプトを一切書かなくても、Unityでゲームロジックが組めるということを売りにしていますが、自分の場合は、普段C#でスクリプトを組んでいるので、そのメリットがちょっと見えづらいという点がありました。

いろいろいじってた結果、Playmakerはゲームロジックのひとつである、状態変移に特化しており、その代わりに非同期処理や複数条件での分岐処理(n-stateを含む)を書くのが難しいことが分かりました。

またC#側ではメリットデメリットが逆転している(状態変移を書くと、Update関数内が変移チェック変数でごちゃごちゃする)ので、これらを使い分けると面白いのではないかと考えたわけです。

 

というわけで、自分の勉強もかねて、C#とPlaymaker両方を使って、状態変移の制御を行う簡単なやり方について考えてみました。
内容の理解度に関しては、一通りPlaymakerを触っている人が対象になります。
アルゴリズムについてですが、

  1. PlaymakerからGameObject(Prefab)のインスタンスを作成する。(1秒ごとに生成するループ)
  2. 生成されたGameObjectは一定時間(ランダム性あり)が来たら自己消滅する。その際にPlaymaker側に消滅したことを知らせる(カウンタを増やす)
  3. Playmaker側は一定数の消滅が来たら、生成ループから外れて、自己消滅する

という構成になっています。

まずはPlaymaker側の設定です。適当な空のGameObjectをHierarchyに追加し名前を"CubeControllerFSM"にし、PlaymakerのFSMを作ります。また、ついでにもうひとつ、"CubeOrigin"という名前の空のGameObjectを作っておきます。
シーンに初めてFSMを作った際の名前は、"FSM"になっていますが、それ以降は変わってきますので、この辺はスクリプトを制御する際に他のFSMと混同しないよう、FSMの名前は意識しておいたほうが良いと思います。

一つ目のActionはCreate Objectになります。このActionは実行時にGameObject(Prefab可)のインスタンスを作成する機能を持っています。Game Objectの箇所には生成したいGameObjectを、Spawn Pointには生成する原点となるGameObjectを設定します。
Store ObjectにはPlaymakerのVariablesの変数と紐付けできますが、増減するものに対しては使いづらいので、今回は無視します。
スクリーンショットでは既にCubeというGameObjectがつけられていますが、後でそのPrefabについて説明します。

二つ目のActionはInt Compareです。これはVariables中のint型の変数が、ある一定の数値未満(Less than)、同値(Equal)、それよりも大きい(Greater than)に、処理を分岐する機能です。

ここでは、IsNotOVERとIsOVERというカスタムEventを作っていますが、ラベル付けの役割だけですので、名前は何でも良いです。
IsNotOVERのほうは、WaitのActionに紐付けされ、IsOVERのほうはDestroy Selfに紐付けされています。
今回は変数にNumOfDestroyという変数を作っています。Variablesタブから変数を作っておきましょう。作ったらInteger1にNumOfDestroyをセットしましょう。

三つ目のActionはWaitです。あまり説明することは少ないですが、Waitは結構重要で、他のActionのLogicのうち、*** Changed系のActionがある場合は、これをかまさないとうまく動かないケースがあります。また、今回はPlaymakerが無駄にインスタンスを生成しないように、Waitを入れています。

このActionは一つ目のCreate ObjectのActionと紐付けされており、ループ処理が行われます。

最後のActionは、Destroy Selfになります。C#で言うDestroy(gameObject)を呼ぶのと同じ機能です。これはそのままなので、画像は省略します。

さて、今度はC#のスクリプト作りです。

名前は何でも良いですが、この記事では"FSMListner"というスクリプトにしています。コードは以下のとおりです。スクリプト自体は、上記の2の機能そのものです。

using UnityEngine;
using System.Collections;
using HutongGames.PlayMaker;

public class FSMListner : MonoBehaviour {

    PlayMakerFSM fsm;
    public string PlaymakerObject;
    public string FsmName;
    public string ParamName;

    float lifetime;

    // Use this for initialization
    void Start () {
        fsm = GameObject.Find(PlaymakerObject).GetComponent<PlayMakerFSM>();
        if(fsm == null && fsm.name != FsmName) {
            Debug.Log("FSMListner: " + PlaymakerObject + " is not found!");
        }
        lifetime = 1.0f + Random.value;
        Vector3 vec = new Vector3();
        vec.x = (Random.value - 0.5f) * 2f;
        vec.y = (Random.value - 0.5f) * 2f;
        gameObject.transform.position = vec;
    }
    
    // Update is called once per frame
    void Update () {
        lifetime = lifetime - Time.deltaTime;
        if(lifetime < 0.0f) {
            if(fsm != null) {
                fsm.FsmVariables.GetFsmInt(ParamName).Value += 1;
            }
            Destroy(gameObject);
        }
    }
}

Start時に自己消滅までの時間を設定し(lifetimeがそれです)、生成位置をランダムに設定しています。また、

fsm = GameObject.Find(PlaymakerObject).GetComponent<PlayMakerFSM>();

の行で、PlaymakerFSMクラス(Playmakerの実体のクラス)から、特定のFSMを抽出できます。
Update関数内では、時間経過による処理のプログラムを書いています。Destroyを呼ぶ前の行の、

fsm.FsmVariables.GetFsmInt(ParamName).Value += 1;

で、FSM内のVariablesにアクセスし、値を書き換える(インクリメント)しています。

スクリプトは保存して、Assetにおいておきましょう。

次にCubeのPrefabとHierarchyについてです。

HierarchyにCubeのGameObjectを作成し、そこに"FSMListener"のスクリプトをD&Dし、CubeをAssetに追加し、Prefab化しておきます。一度Prefab化が終わっていれば、HierarchyからCubeを削除しても大丈夫です。
Inspectorの"FSMListener"の項目に、public変数がありますので、それぞれ、

Playmaker Objectには、"CubeControllerFSM"、
Fsm Nameには、"FSM"、
Param Nameには、"NumOfDestroy"

を忘れずに入れておきましょう、すべて文字列になっていますので、手打ちで文字列を
入力してください。

これが終わったら、再度CubeのPrefabを、Playmakerの一つ目のCreate ObjectのGameObjectにセットしておきましょう。

終わったら、実行して、どんな動作をするか確認しましょう。

実行中はCube(Clone)が生成され、消えては生成され、という具合になり、最後はCubeがすべて消え、CubeControllerFSMも消滅するという挙動になると思います。

さて、今回はPlaymakerとC#両方の操作で互いに影響するコードについて解説をしました。
今後もこのように、互いのメリットデメリットを補完することにより、より楽しいUnityコーディングができるようにいろいろ考えていこうと思います。


MikuMikuNadeNade(仮)について(更新中)

2013年12月04日 | CG関連

本記事はOculus Rift Advent Calendar 2013に向けられた記事です。

12/05担当で、記事はまだ更新途中ですが、時間が迫ったのでたちまち公開させて頂きます。

MikuMikuNadeNade(仮)について

1.概要

2013年10月未明、先生からNovint Falconを借りたものの、その応用が見つからず、困っていました。初め考えていた応用例は入力デバイスとして、3Dモデルを造形するソフトを考えていましたが、Novint Falconのコントローラは移動のみで、回転運動が表現できないため、利用方法までは思いつきませんでした。

その後、MikuMikuAkushuという革命的な応用例が出て、自分も同じように仮想人物に接触するツールとして使えないか?という方向性にシフトしました。

2.Novint Falconの性質

NovintFalconはハプティックデバイスとして、入力として現在位置の検知、出力としてモーターのトルクの反力を得ることができます。ただし、すべて直線運動に変換され、コントローラもがっちりと固定されているので、ワイパーをかく運動のような動きになるので、応用が限られます。

3.作るきっかけについて

MikuMikuNadeNade(仮)を作る経緯に至った理由についてですが、これはOculfes出張版 in 徳島マチアソビでMikuMikuAkushuを触り、改めて次の事が気になったからです。

ミクさんを見つめながら、自分の手で頭を撫でる事が出来たら、よりミクさんを身近に感じられるのではないか?

難しい言い方をすると、

VR空間上の物体への接触に代替品を用い、またHMDを使ったVRで代替品である事の認識をぼかしてしまう事により、それがあたかもVR空間上の物体に触れているという感覚が得られないか?また、こちらのアクションに合わせてフィードバックを返してくる仕組みがあると、VRにおいて現実感を付与するのに貢献できないかと考えたわけです。

4.なぜ「頭を撫でる」のか?

人間の触覚で面積あたりで一番感覚を強く感じるのは舌ですが、さすがにそれだと衛生上の問題がつきまとうので、二番目に強く感じる、指先が使えるようにすることを考えました。

そのためにはコントローラなどを手で握り込んでしまうと、指先が使えなくなってしまうので、支える必要が無いようにかつ、 触れるものというふうに考えました。

基本は撫でるコンセプトは固まっていたので、あとは何を撫でるかを考えていたところ、人の頭にしました。普段子どもを相手にしていない限り、人の頭を撫でる機会はそうそうありません。

お互いに癒しの効果があって、なおかつ手に入れやすいものとして、ヘアメイク用のマネキンとウィッグならなんとかなるだろうと思い、人の頭の触覚を目指しました。

5.試作と問題点

発泡スチロールのマネキンを用意し、ウィッグをかぶせて、なんとかNovint Falconに括り付けたところ、大きな問題に当たりました。

頭を撫でる動きは球面を撫でる動きに近く、撫でる時に摩擦が発生しそれによりその摩擦による反作用力が回転運動となり、手に頭が付いてくるという事態がおきました。

Novint Falconのコントローラはほぼ球形のため、支えて固定する箇所が無く、マジックテープを使って遊びがあったため、かなりグラつき、撫でる事自体が難しくなっていました。

この後惨事が…

6.改良?

マネキンが胸像モデルだったので、まずはこれを取り去って、なるべく大きな回転運動にならないように、思い切って眼よりも下の部分を切り取るという事をしました。これによって見栄えは凄く悪くなりましたが、手に頭が付いてくることも少なくなりました。

ギャー!!

7.撫でている、という状態の検出ギミック

画面向こうのミクさんに撫でていることを知らせるために、いくつか案を考えましたが、手に何かを装着するタイプだと触る事に集中できないと思っていましたので、撫でた時に発生する音を拾うようにしました。

Unity上でマイクのボリュームを拾うコードはこちらを参考にしました。#junki::acoustic - Unityでマイクのボリュームをリアルタイムに取得するAdd Star

ミクさんを撫でた時に、表情が変わるようにし、撫でているという印象を与えているものを付加しました…がもうすこし改善する必要がありそうです。

参考までに、ミクさんでの撮影が間に合わず、同じモーションを使っているHonoka Futabaモデルを使ったナデナデ動画を掲載します。改良版をお楽しみに!

8.感想、今後の課題

仮想空間上に自分の手が表示されていないので、手の位置の感覚が曖昧になってしまっています。これをなんとかセンサーを腕に付けずに改善できれば良いなぁと思っています。やはりkinectかWebカメラで手の位置のセンシングなどが現実的ではないかと考えています。

次の日は@takashijonaさんです。

よく分かるかもしれないOculus Riftについて(2) - Oculus Rift向けのポストプロセスの部分について

2013年07月06日 | CG関連

はじめに

Oculus Rift向けの公式に対応している開発環境はUnity Proですが、今はTrial版も出ているため、触っておられる方も多いと思います。
しかし私は、はじめからこのソフトは使うことを考えてはいませんでした。それでもなんとか、見れるようになりましたので、解説をして行きたいと思います。

魚眼モデルスクリーンについて

人間の目は、光学レンズとしての能力に忠実であり、Oculus Riftもその性質を生かして設計されています。 まず、魚眼モデルでは、人間の目に対して、半球状の投影スクリーンを投影している状態と同じです。Oculus Riftは平面モデルの液晶を持っていますが、これをレンズでうまく加工して目の前に半球状の投影スクリーンを置いているように見せかけています。 

 

 

極座標系への変換

図中の青い部分はCGでの撮像面、つまり映像そのものであり、赤い部分は魚眼モデルでの撮像面となります。カメラは本来、赤い部分の撮像面が平面に射影するため、光学的な歪みを持っていますが、レンズの性質やソフトウェアでこの歪みを無くす処理がされています。Oculus Riftでは、この歪みの状態を再現する必要があります。  撮像面の大きさをwとする時、計算するための撮像面をw=2uとおき、魚眼モデルの撮像面位置に対応する平面モデルの位置を計算します。

平面モデルの単位である1uは直交座標系、魚眼モデルの単位である1ucは極座標系のため、対応点を見つけるには座標変換をする必要があります。図中の点pは3次元空間上の点、点p'はCG映像や写真で撮像された面での対応点になります。OculusRiftでは、p''が対応する点となります。

 

ここでの解説は、p''の位置に対応するp'を求めるための方法について解説します。まずはfocal legnthを計算します。ここでの焦点距離の定義は、カメラ光学と同様に焦点から撮像面までのことを差します。

focal lengthが1、視野角の角度をθとする時、撮像面の大きさはTan(θ/2)となります。Tanの性質上、90°は求められないため、算出できる視野角は1°~189°までとなります。ここで、撮像面の大きさを単位数(1u)にした場合、focal lengthは Cot(θ/2)*u = 1/Tan(θ/2)*uで求めることができます。これによって、1uに対する比率としてfocal lengthを得ることができます。

1ucは画面中心からの視野の最大角の単位(ラジアン)となり、視野角をθとすると、1uc = θ/2となります。つまり、半球上の極座標点 p''は -θ/2<=p<=θ/2の区間で表すことができます。

よってp''のに対応するp'の位置はp'= f * Tan((θ/2) * p'')となります。視野角を110°として実数に直すと、f * Tan(55° * p'')になります。p''が1ucの時、Tan(55°) * f = 1uとなり、これにより、半球型の撮像面上の点から平面型の撮像面での対応点が求まります。

 

 

撮像面の変形

また、魚眼モデルの座標系は極座標系なので、常に焦点から撮像面までの距離を一定にする必要があります。これには撮像面を変形させる必要があります。
水平部分(y軸の値が0)では先ほどのモデルが通用しますが、ここでもレンズモデルが極座標系なので、極座標系から直交座標系に変換した時に樽型の歪みが生じます。
極座標系では画像中心からの距離と角度によって表されるため、p’’(θ,l)の極座標系の対応点をp’(x,y)とおくと、x^2+y^2 = l^2となり、p’=(cos(θ)*l,sin(θ)*l)で計算する事ができます。ただし、これは水平視野角、垂直視野角がいずれも180°の時になりますので、OculusRiftでは水平視野角が120°、垂直視野角が90°であり、計算が複雑になってしまいます。

樽型に歪ませる関数として、厳密に極座標系から直交座標系の式を導入する必要がない場合は、簡略式で済ませてしまいます。ただし、この方法はxとyがそれぞれ-1から1までの範囲を取るようにプロジェクション変換をする必要があります。

x’ = cos(y*垂直視野角(ラジアン)*歪曲強度 )* x;
y’ = cos(x*水平視野角(ラジアン)*歪曲強度 )* y;

cos(0)=1、cos(180°)=0なので、式を見るように、 yの値が大きいときはxの値が小さくなるようになり、xの値が大きいときはyの値が小さくなるように計算されます。
ただし、厳密的に証明している訳ではないので、歪曲強度のパラメータをいじって調整する必要があります。

この二つの方法により、擬似的ですがOculusRiftのポストプロセッシングを再現する事ができます。

GPUによる処理コード(GLSL)

最後にQuartzComposer上で書いたGLSLコードを紹介します。中には解説に登場しなかった変数も出てきますが、dist、distTX、distTY、は1から調整し、xshiftは0、xscale、yscaleは1から、offsetxも0からで調整していけば具合が良いと思います。参考になれば幸いです。

textureはレンダリングしたイメージ(水平視野角110°、垂直視野角90°以上でレンダリングしたもの)をバインドし、VertexShaderのメッシュは-1から1にスケーリングされたグリッドを使っています。分割されていればされているほど綺麗にレンズ状になります。

 

----ここからVertex Shader----
const float PI = 3.14159265358979323846264; //Constant Pi
uniform float fovX; //Field of View X
uniform float fovY; //Field of View Y
uniform float dist; //lens distortion(1.0~)
uniform float distTX; //another distortion(~1.0) horizontal texture magnification
uniform float distTY; //another distortion(~1.0) vertical texture magnification
uniform float xshift; //horizontal gaze shifthing(default value is 0)
uniform float xscale; //horizontal scale
uniform float yscale; //vertical scale
uniform float offsetx;//horizontal offset(a margin between both images)

//float fovX = 55.0; //Field of View X
//float fovY = 45.0; //Field of View Y
//float dist = 1.4; //lens distortion(min value is 1.0)
void main()
{
    //focal legnth = 1.0/2.0*cot(fov/180.0*PI) = 0.5/tan(fov/180.0*PI)
    //float focalX = 0.35010376910485486; //110(55) Degree focal length
    //float focalY = 0.5; //90(45) Degree focal length
    float focalX = 0.5/tan(fovX/180.0*PI);
    float focalY = 0.5/tan(fovY/180.0*PI);
    //vec4 pos = gl_Vertex * gl_ModelViewProjectionMatrix;
    vec4 pos = gl_Vertex;
    
    vec4 tpos = gl_TextureMatrix[0] * gl_MultiTexCoord0;
    //vec4 tpos = gl_MultiTexCoord0;
    
    float px = pos.x;
    float py = pos.y;
    
    float tx = (tpos.x - 0.5)*2.0;
    float ty = (tpos.y - 0.5)*2.0;
    
    float dx = cos(dist*py*fovY/180.0*PI)*px;
    float dy = cos(dist*px*fovX/180.0*PI+xshift/180.0*PI)*py;
    
    float ox = focalX*tan(distTX*tx*fovX/180.0*PI);
    float oy = focalY*tan(distTY*ty*fovY/180.0*PI);
    
    gl_Position.x = dx*xscale+0.5+offsetx;
    gl_Position.y = dy*2.0*yscale;
    gl_Position.z = 0.0;
    //gl_Position = pos;
    
    gl_TexCoord[0].x = ox+0.5;
    gl_TexCoord[0].y = oy+0.5;
    //gl_TexCoord[0] = tpos;
        
    //Forward current color and texture coordinates after applying texture matrix
    gl_FrontColor = gl_Color;
}
----ここまでVertex Shader----
----ここからPixel Shader----
uniform sampler2D texture;
void main()
{
    //Multiply color by texture
    gl_FragColor = gl_Color * texture2D(texture, gl_TexCoord[0].xy);
}
----ここまでPixel Shader----

ほか、上記のコードを使った参考動画


【python】matplotlibを使って画像のカラーヒストグラムを出力

2009年11月09日 | CG関連
MathematicaのHistogram3Dは見にくくて、bivariateな分布が見辛いので、無理矢理ListDensityPlotにしてましたが、かなり遅いので、他の方法を探してたら、pythonでmatplotlibなるものを発見。使ってみました。
本来は違うグラフを書くのが目的でしたが、練習として画像のrgbヒストグラムを出力するプログラムを書いてみました。

--------------以下ソース--------------
import sys, pygame
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt

pygame.init()
pygame.display.set_mode((150, 150), 0, 32)
buf = pygame.image.load(sys.argv[1]).convert()

rect_hist_b = [0.10, 0.03, 0.85, 0.25]
rect_hist_g = [0.10, 0.36, 0.85, 0.25]
rect_hist_r = [0.10, 0.69, 0.85, 0.25]

axHist_b = plt.axes(rect_hist_b)
axHist_g = plt.axes(rect_hist_g)
axHist_r = plt.axes(rect_hist_r)

w, h = buf.get_size()
r = np.zeros((w*h,), dtype=np.int)
g = np.zeros((w*h,), dtype=np.int)
b = np.zeros((w*h,), dtype=np.int)

for j in range(h):
	for i in range(w):
		#color = (R, G, B, A)
		color = buf.get_at((i, j))
		r[j*w + i] = color[0]
		g[j*w + i] = color[1]
		b[j*w + i] = color[2]
		
axHist_r.set_title('red component')
axHist_g.set_title('green component')
axHist_b.set_title('blue component')
axHist_r.hist(r, 256, normed=1, facecolor='red')
axHist_g.hist(g, 256, normed=1, facecolor='green')
axHist_b.hist(b, 256, normed=1, facecolor='blue')

axHist_r.set_xlim(0, 255)
axHist_g.set_xlim(0, 255)
axHist_b.set_xlim(0, 255)

plt.savefig(sys.argv[2],dpi=72)


--------------コマンドプロンプト--------------

$:python hist.py [入力画像] [出力画像]

--------------

プログラムを使うとこんな画像が

こんな感じに出力されます。

繋ぎネタ>3Dビューワーをiアプリで作ってみた

2008年12月27日 | CG関連
今iPod touchのプログラミングを勉強しているのですが、Objective-Cの特殊な記述に悩まされています。またCocoaのプロセス間通信が従来と異なることができるみたいで、かなり面白くできそうです。
…またiアプリと同じような制限があったら嫌なんですが。

ということで、しばらく勉強のためまた更新できそうにないので、繋ぎとしてニコニコに上げたネタを流用しようと思います。

3Dビューワーをiアプリで作ってみた


前に書いた記事の延長上のもので、一応形になったので再度ネタにします。配布も可能なので、興味があれば使ってやってください。

※自己責任でダウンロードお願いします。後iアプリDL 、設定ファイルDLのリンク以外はAdリンクなので気をつけてください。
http://catman.shakunage.net/viewer/Download.html

iアプリ+OpenGL ESを勉強中

2008年02月09日 | CG関連
携帯で3DCGのモデルを表示したいので、目下java(doja)を勉強中です。
しかしjavaのコードの書き方はWinFormと似てるようでぜんぜん似てませんね。

とりあえず今はOpenGLの初期化命令がよく分かってません。
リファレンスを見ても、initみたいな初期化命令がありません。
javaでOpenGLを初期化する方法ってC++やC#とは違うのか・・・?



2008/2/10追記

OpenGL ESを使ったアプリは雑に言えば許可された会社じゃないと配布できないみたいです。
念のため通常の3Dを表示するためのGraphics3Dインターフェースに関しても調べてみましたが、形状のデータに使われるクラスが抽象化されていて、なんと頂点などの情報の変更が出来ません。(頂点を設定する関数すらない)しかも形状データにあたるObject3Dのインスタンス生成に使われるバイナリファイル(おそらくMascotCapsuleのd4d形式)のフォーマットも公開されていないため、現状では有料のソフト(Mayaや3dsMaxなど)のソフトを使って出力する必要があります。

つまりハッキリ言えば「個人で3Dのアプリなんか作ってんじゃねーよ」という態度が開発環境に見られます。

スキンメッシュアニメーションを作ってみたかったのですが、現状の環境では無理かもしれません。おそらくFigureクラスがスキンメッシュの処理を持っていますが、どんな処理を行っているのか検討もつきません。

配布できなくてもOpenGL ESを使ってやるか、それともあきらめるか考えています。

久しぶりにメタセコ弄り

2007年06月19日 | CG関連
UVアンラップを考えないでガリガリハイポリ気味で作ってます。
テクスチャで影やシルエットを書き込む必要が無いので楽なんだけど、
みるみるポリ数が増えてアプリが重くなっていくのが玉に瑕。
以前5000poly超えたモデルをUVアンラップしようとして地獄を見たことがあるので、
今回はできるだけアンラップせずに作れるように考えてみます。

ペン

2007年01月19日 | CG関連
漫画を描くわけじゃないんですが、漫画用のペン先と安価なペン軸を買ってみました。早速使ってみたわけですが、万年筆みたいにすらすらと線が引けない。いろいろと試してたらようやく楽?に線が引けるようになりましたが、コレ結構力要りますね。

ペン先はGペンといって、線を引く時の強弱で太い線や細い線が自在に引けるというもので、漫画家の方は大体顔の輪郭などを書く時に使っているみたいです。ほかにも丸ペンといって細い線を書くのに便利なペン先もありますが、とりあえずまずはGペンを使ってみたかったというのと、丸ペンのペン先はちょっと高いので、今回はGペンだけを買ってみました。

遊びながら線を引いていく時に、気がついたのですが、ペン先は中心の穴から先まで二つに割れていて、力を入れるとその割れ目に隙間ができて、その間にインクが入って太い線が引けるんですね。

コンピュータを使って絵を描いたりすると、こういったアナログの物は返って新鮮に感じます。それもまたつかってて楽しいというのがありますね。
intuosなどのタブレットはこういったアナログのものに近づけようという目標があるのでしょうが、なかなか難しいでしょうね。

Wiiのコントローラー(ヌンチャク)はパワーグローブの再来?

2006年11月29日 | CG関連
上の画像は昔持っていたファミコンロボットをはじめて動かした時の自分です。

米国でWiiをはじめて購入された方のミドルネームが「トライフォース」という名前だったのは知っていましたが、パワーグローブを手にはめてたというのは知りませんでした。
さて、最近になってまた『変』ガジェット熱がでてきたので、ちょこっとWiiの仕様を調べてみました。
---------------------------------------------------------------------------
Wiiのコントローラーはセンサーバーとセットで使わないと動作しません。このセンサーバーは実は『センサー』という言葉がついているのは名ばかりで、実際にはセンサーの類は付いていません。

Wiiのセンサーバーはろうそくで代用可能

さて、なぜ蝋燭なのかというと、蝋燭は可視光のほかに不可視光も出しています。不可視光は一般的に赤外線、紫外線のことを言います。蝋燭は見える光のほかにも赤外線を放出しています。これをリモコンがキャッチして認識をしているのです。つまりリモコンがセンサーになり、センサーバーがコントローラーになっているのです。
#(実際のプロセスは、センサーバーの光をリモコンがキャッチ→リモコンのボタン操作と位置情報を本体に送る。という感じです)

話は逸れましたが、私はこのWiiのコントローラーがパワーグローブの再来ではないか?と思っています。


PowerGloveの解析
元サイト
Art & Science Laboratory
ブログでなくホームページのためリンク許可の要求をする必要がありますが、記述が無いので勝手ですが記事へのリンクを張らせていただきます。

上のサイトを見てもらえれば今までゲテモノ扱いされていたあのパワーグローブが実際はどんなすごいものだったか分かると思います。このパワーグローブのコントローラーとセンサーのやり取りは超音波ですが、データの種類が違うだけで、根本のシステムは一緒ということになります。
----------------------------------------------------------------------

しかしWiiの仕様はファミコン時代を彷彿とするような内容が多いですね。任天堂が唱える『ゲームの原点に返る』とはファミコンのことなのでしょうか。