Edited at
D言語Day 13

私は初音ミクに会いたかった

More than 3 years have passed since last update.

D言語 Advent Calendar 2014 13日目の記事です。


事の発端

2012年末、暇を持て余したひとりの無職が呟いた。

そう、これが長い旅路の始まりである。


この記事に書いてある事

殆ど自分の作業過程を文字に起こしているだけなので、ガチな D言語要素は薄くなってしまっています。

ただ、D言語で 3Dな何か作りたいな〜って思ってる人へ、少しでも情報提供なりが出来たら嬉しいなと思います。


出てくるもの


  • D言語 ( http://dlang.org/index.html )

    私は、D言語が好きです。


  • MMD ( http://www.geocities.jp/higuchuu4/ )

    ボーカロイド界隈から爆誕した PV作成用ソフトウェア。

    何が良いかって可愛いが沢山詰まってる。夢がある。


  • OpenGL ( https://www.opengl.org/ )

    男は黙って OpenGL直叩き。(つらい

    環境毎に、というか使ってる GPU如何によって使える機能が結構変わってくるので、

    自分の環境がどの Ver.まで対応してるかまず調べましょう。

    といっても 2.0くらいは大体サポートされてるでしょうし、簡単な所から触るなら十分だと思います。

    ちなみに私は GLSLとか使い方知らないです。


  • GLFW ( http://www.glfw.org/ )

    モダンな OpenGLラッパー。

    GLU/GLUTと双璧みたいな立ち位置。

    同時に利用出来るかどうかは、試してないので分からない。

    初期化とか Windows生成とか書く敷居がガクっと下がる。

    GLUTと比べると機能貧弱感あるけど、その分軽い。

    貧弱とは言っても Support for keyboard, mouse, gamepad, time and window event input, via polling or callbacks とか書いてあるし、実際使えるし十分便利。

    個人的にはおすすめ。


  • Derelict ( https://github.com/DerelictOrg )

    D言語で 3Dもといゲームっぽいの作るなら大体お世話になると思うライブラリ群。

    上記の OpenGLや GLFWの他、色々面白いポーティングがされてるので眺めてみましょう。


  • dub ( http://code.dlang.org )

    D言語のビルドツール。

    上記の Derelictを含む、D言語で実装された各種ライブラリを手軽に使える良さがある。

    こちらも一通り眺めてみる事をおすすめします。


  • Collada ( https://collada.org/ )

    3Dモデルの数ある規格の内のひとつ。

    各種 3DCGソフトウェアの独自フォーマット間を変換する為に生まれたようなやつ。

    拡張子は .dae。

    対応してるソフトウェアは結構ある。

    実際に使われてるかは存じませぬ。

    PDFもあるよ -> https://www.khronos.org/files/collada_spec_1_5_0_jp.pdf


  • MMDFromCollada

    MMDにて利用されている

     .pmd(最初期のモデルデータフォーマット)

     .pmx(.pmdの拡張フォーマット)

     .vmd(モーションデータ)

    辺りを Colladaフォーマットに纏めて変換してくれる素敵なソフト。

    これ使って MMDのモデルデータの中身を読んだ事が、ここに至るまでの大きな動機になった。



作業中のリポジトリ

https://github.com/ottu/ColladaLoader

MMDfromColladaを使用して作成した .daeを表示させる為に頑張ってる。

このリポジトリが出来てからここまでの苦難の道をここに記そうと思う。


以下、作業工程


まず Lat式ミク(.pmd)を表示しようと思った。可愛いは全ての原動力になりえる。

Lat式ミクを変換した .daeをひたすら読む。MMDモデルの内部構造の仕様を只管漁る。

MMD自体はソースコードの公開はされていないものの、有志の尽力のお陰でモデルデータの内部フォーマットは実用レベルまで解析されている。

PMXEditor (http://kkhk22.seesaa.net/category/14045227-1.html) に

フォーマットの詳細乗ってるテキストが入ってるので、とても助かっている。

当時の std.xmlはあまりにもパフォーマンスが出てなかったので、xmlライブラリ書く。

無職には無限の時間があった。しかし後にこれは使わなくなる。当然である。今は反省している。

取り敢えず .daeを表示出来るようにする為、Colladaの仕様書読みながら必要最低限の読み込み部分を書く。

https://github.com/ottu/ColladaLoader/tree/master/import/collada/import/collada

model.d 以外は全部 Collada形式のデータを扱う為の struct他が入ってる筈。

Collada自体は仕様として大体の構造を格納出来るように設計されているが、

MMDのモデルにおける Meshは Trianglesと Verticesしか使ってないのでそれしか実装してない。

( ここら辺 -> https://github.com/ottu/ColladaLoader/blob/master/import/collada/import/collada/geometry.d )

.daeの読み込みは出来た。しかし ottuには OpenGLがわからぬ、わからぬ。


D言語で OpenGL…なるほど Derelict。

この頃にはまだ dubが無かった筈で、Derelict使ってにしても一体どうやってコンパイルしてたか全く思い出せないので、今改めて dubは便利だなって思ってる。

初期化周りとか面倒臭そうじゃん、と思って GLUとか GLUTとか使ってみようとする。

分からんかった。

ティーポットすら出せなかった。

SDL使うかーと思っていた所で GLFWを知る。

void main( string[] args )

{

writeln("glfw initialize...");
if( !glfwInit() )
{
writeln("Initialize faild.");
return;
}
scope(exit)
{
writeln("glfw terminate...");
glfwTerminate();
}

writeln("open new window...");
g_Window = glfwCreateWindow( 640, 480, "test".toStringz, null, null );

if( !g_Window )
{
writeln("Open window failed.");
return;
}
scope(exit) {
writeln("window close...");
glfwDestroyWindow( g_Window );
}

glfwSetKeyCallback( g_Window, &KeyFunc );
glfwSetCharCallback( g_Window, &CharFunc );
glfwSetMouseButtonCallback( g_Window, &MouseButtonFunc );
glfwSetCursorPosCallback( g_Window, &CursorPosFunc );
glfwSetScrollCallback( g_Window, &ScrollFunc );

glfwSetWindowSizeCallback( g_Window, &WindowSizeFunc );
glfwSetWindowCloseCallback( g_Window, &WindowCloseFunc );
glfwSetWindowRefreshCallback( g_Window, &WindowRefreshFunc );

...

まぁなんて簡単なの!

( Derelictは各種コンポーネントの初期化呼び出しをする必要があるのだけど、

 これに関しては model.dの shared static this() { ... } 内で呼び出すようにしている。

 https://github.com/ottu/ColladaLoader/blob/master/import/collada/import/collada/model.d#L1848 )


ベースは揃った。モデルの描画に挑戦する。

頂点座標だけを描画する

なるほど分からん。

.daeと睨めっこしたり、ただの三角形モデルの .dae作ったりして頂点の配列順を調べる。

モデルデータに則って頂点を線で繋ぐ

発狂したラミエル(序)みたいな物体になる。首が。足が。悲しい。

どうにかこうにか頑張る

人型っぽくなる。

この時点で、3Dモデルって本当に沢山の三角形で表面覆われてるんだなぁーって感じたのを今でも覚えてる。

テクスチャ貼ろうと試みる

画像データを読み込んで、それをそのまま OpenGLの関数に渡せる良いライブラリは無いかなーと思って調べた結果、DelerictIL使う事にした。

( DevIL ( https://github.com/DentonW/DevIL ) のラッパー )

画像の表示出来た。

画像読み取る頂点配列を処理する順番間違えてミクさんがえらい事になったりした。

ミクさんぽく表示出来た

本当に嬉しかった。

しかし何だろう…頬の輪郭とか鼻筋とか真っ黒なんだが…?

Lat式にはエッジというものが追加されており、それの影響だという事は分かった

詳しくはエッジ部分だけ法線が逆転してて、その部分はブレンド処理でよしなにしてやらなければいけないらしい。

面倒 難しそうだからやってない。

エッジ無し版の Lat式ミクも表示させてみたが、同じ箇所の表示がやっぱり変なまま。ぐぬぬ。

あぴミク(.pmx)表示させてみるかーと思い立つ

当然上手く表示なんて出来ない。

.pmdと .pmxではどっかの頂点配列の X軸の符号が反転してて、そこの切り替えをやってあげなきゃいけなかった。

ボーンの可視化

モデルデータ内には、各関節にあたる座標情報しか登録されていないので、それを繋げて便宜上ボーンと呼んでいます。

人体における骨格の代わりになる部分ですね。


あぴミクはそれなりに表示出来た。やった。可愛い。僕はあぴミクに魂を売った


さてモデルは表示出来た。次は踊らそう

やっぱこれっしょ! ( http://www.nicovideo.jp/watch/sm19984470 )

本当に余談だけど、ColladaLoader実装開始直後には一日中これ流しながら作業してた気がする。

ただこのモデル、製作者の意に反して改造と服装チェンジが行われてるっていうので、動画ごと一度削除されたという過去がある。

私が見ていた時には二人ともポニテだったし、こんなに衣装多くなかったんだがなぁ…

球面線形補間、クォータニオン、CCD-IK、なるほどね?

モーションデータには「ある時間 nの時に、指定したボーンをどれくらい回転させるか」という情報が格納されてる。

けどそれが思った以上にざっくりな値しか入ってない。

で、その情報に合わせてモデルのボーンを回転させていく訳だけど、何も手を加えないで実装するとカクカク人形みたいになってしまう。

それを解消し、スムーズに動かす為に必要な実装が"球面線形補間"であり"CCD-IK"であり、

それらを実装するにあたって使えると便利な概念が"クォータニオン"。

球面線形補間 is 何

最初に直立姿勢を取って下さい。

その時、右肘にセンターが付いてると思って下さい。

3秒かけて腕だけ"スペシウム光線"の姿勢になって下さい。

センサーの軌跡が"球面線形補間"そのものです。

先に線形補間について補足

Linear Interpolation とか lerp とか呼ばれるやつ。

始点 0秒の時点で座標(x,y) にある点を、終点 3秒の時点で座標(x+3, y+3) まで移動させるとする。

この時、始点座標と終点座標、それと移動に要する時間が分かっているので、

中間点 n秒の時点での座標は (x+n, y+n)だと機械的に計算出来る。

改めて球面線形補間

Spherical Linear Interpolation とか Slerpとか呼ばれるやつ。

先ほどの"スペシウム光線"を思い返しましょう。

右肩を座標(0,0)とし、向かって正面方向を X軸の正方向、頭上方向を Y軸の正方向とします。

始点 0秒時点での右肩を座標(0,-3)とします。あなたの上腕骨の長さは 3です。

終点 3秒時点では右肘は座標(3,0)にありましたね。

では、線形補間を使って中間点 2秒時点での右肘の座標を計算しましょう。

はい、(2, -1)ですね…っ!ちょっと!あなた上腕短く(=1.73205...)なってますよ!

人間は関節を捻じ曲げて動く。骨は伸びない縮まない

人間の各部の可動域は骨と筋肉によって制限されている為、無理な姿勢は基本的には取れないようになってます。

でも画面の中にいるミクさんは違う。物理的な骨も筋肉も無い。

ちょっと間違えれば腕は伸びるし膝は逆関節になるしエクソシストのリーガンちゃんもびっくりな体勢にだってなれる。

でもそれじゃあんまりにもあんまりですよね。ミクちゃんにはずっと可愛くいて貰いたいですよね。

肩を中心と見た時、肘の可動範囲は"肘から一定距離離れた点を結ぶ円運動"に限られます。(これでも人間の可動域は越えますが

肘に限らず、あなたの関節は全て円運動の範囲からは逃れられないのです。

そんな時に使うのが球面線形補間です。

関節Aを中心に見て、それに連なる端点の始点座標Xから終点座標Yへの移動を計算します。

この時重要なのは、3Dモデルにおける端点(この場合は右肘)の座標の計算は、

その端点自体の移動ではなく、親関節(この場合右肩)の回転で計算するという事。

これも 3Dモデルの成り立ちとかの話になりますが、MMDのモデルも例に漏れず、

基本的には腰あたりにある"全ての関節の親になる根点"が存在し、全ての関節/末端は辿っていくとその根点まで辿れるように設計されています。

その設計により、全身の可動は「根点の回転運動 x その子点の回転運動 x その子点の回転運動 x ... x 末端の回転運動」で表現が可能になっています。

ミクさんにお辞儀させるとするなら、腰辺りの端点を X軸回転させる事により、相対的にそれに繋がってる鳩尾辺りにある端点が円運動して、結果上半身が前に折れる。

"スペシウム光線"の右腕の動きも、3秒かけて右肩を 90度回転して、右肘を 90度回転させて、実現しているという訳です。

そんな処理の繰り返しによって、今あなたの目の前の画面の中では今日もミクさんがあざとい可愛さを振りまいている訳です。

あぁ^~可愛いんじゃ~。

…で、ここまで説明しておいて何ですけど、これ以上を説明出来るだけの知識を持ち合わせていないので後はググってね♡

3次元空間での球面線形補間の公式も調べれば色々出てきます。あとは根気で乗り切ろう。

( https://github.com/ottu/ColladaLoader/blob/master/import/collada/import/collada/model.d#L1194 )

CCD-IK is 何

Cyclic-Coordinate-Descent Inverse Kinematicsとか、単に CCDとか呼ばれるやつ。

調べると学術記事とか沢山引っ掛かる系のアレげなやつ。

これを文字だけで説明するのはやっぱり私には不可能なんですけど、

このアプローチは球面線形補間で説明した「親関節から子関節方向への回転の乗算」とは逆のアプローチによって末端を目的の座標へ移動させる為の手段。

流れだけ書くとこんな感じ↓


  1. 右肩(0,0)を中心とし、上腕骨と橈骨の長さを 3とし、右肘(0,-3)、右手首(0,-6)とする。

  2. スペシウム光線を放つ為には、右手首を(3,3)まで移動しなければならない。

  3. まず右肘(0,-3)と目的点(3,3)を直線で結ぶ

  4. 3.で引いた直線上に右手首を重ねる。(今回はその為に右肘を 150度折り曲げる。

  5. 次に右肩(0,0)と目的点(3,3)を直線で結ぶ

  6. 5.で引いた直線上に右手首を重ねる。(今回はその為に右肩を…何度回転するんだ?ぱっと見 45度くらい? (ここちゃんと計算しないと永遠に計算終わらないよ!

  7. 右手首は目的地に届きましたか?まだ離れてるようなら 3.に戻りましょう。

    目的地に限りなく近い所まで移動出来たなら無限地獄から抜け出しましょう。もうゴールしても良いよ。

IKの実装にあたる問題

CCD-IKの他にも Inverse Kinematicsに分類される計算方法は幾つかありますが、基本的にその全てにおいて

「モデルをあやつり人形のように動かせる」という点において、モーションを定義する側からすると物凄く手間が減る良い手段となります。

わざわざ各関節の回転角度を調整しながら目的の姿勢を作る訳では無く、手首や足首をつかんでぐいぐい動かすようなもんなので。

MMDでは、モデルデータ内に含まれている IKに使うだろう値を見るに CCD-IKが実装されているというのが共通認識なので、

私もそれに倣って CCD-IKの実装を試みた訳ですが、IKの処理を実装するに当たってやはりそれなりのリスクが存在します。

それは「各関節を曲げられる方向の制限」を設けなければいけないという点と、「腕/足を動かす為の計算量が爆発的に増える」という点です。

各関節を曲げられる方向の制限

制限を設けない状態でミクさんにサッカーボールを蹴っ飛ばして貰うと分かりやすいです。

1. ミクさんは右足でボールを蹴ろうとしています。

2. お尻の後方辺りに目的点を置きます。CCD-IKにより右足首をひっぱり上げます。

3. ただしく右足を振りかぶりました。目的点を正面のボールに再設置します。

4. CCD-IKにより足を振り下ろそうとします。

5. 一発目の計算によってミクさんの右足が逆関節になります。

6. 以降、どれだけ計算してもシュートするまで逆関節化が直る事は無いのです…

他にも、膝が側面方向に折れ曲がったりとかしてしまう。

Y軸、Z軸の回転は許されず、X軸の"方向付き 160度位までの回転"のみ許される。

その時、目的点が Y-Z軸平面上にあればまだマシですが、そんな不自然な挙動はあり得ない。

X軸上の変動に対しては、足の付け根の関節を回転させる事で対処をしなければなりません。

あとこんな事呟いてたっぽい


因みにこの場合は初期姿勢の時点から、膝をほんの少しだけでも曲げておいてやるとちゃんと曲がります。

( 各関節が一直線に並んでいる状態では、その直線上で目標点が幾ら動こうとも各関節の回転は常に 0度にしかなり得ない為。

とか色々な要因によって、膝の処理はかなり神経を使う事になると思います。

腕/足を動かす為の計算量が爆発的に増える

またスペシウム光線の右腕の可動を思い出して下さい。

球面線形補間による計算だと、任意の時間における右肩、右肘の回転量は一意に求められますが、

CCD-IKでの計算は近似値をどれくらいの精度にするかによって永遠に処理が終わらない可能性が出てきます。

なので MMDモデルの IK情報には大体ループの上限数が設けられています。( あぴミクの足 IKには 30ループの制限が掛かってます。

ついでに言うと、大体のモデルには足 IKしか定義されてないんじゃないかな?と思ってる。最近のモデルの事情はどうなんだろう…

前までは「腕 IKを追加する用のツール」とかがあった筈なんだけども。

IKは便利。けど実装はつらい

私が選んだ WAVEFILEのモーションデータは、下半身の処理を全て IKで行っていた為、

CCDーIKを実装しない状態では上半身だけ達者に動いて足が棒のまま…みたいな状態になっていた。

悔しくて CCD-IKを実装して、それなりには動くようになったのだけど、

股関節の角度制限をうまく掛けられてない事情によりどんどん内股になっていくという悲しい状態。

皆めげずに頑張って実装しよう!

クォータニオン is 何

Quaternion. 四元数とも呼ばれるやつ。

3Dの世界における回転処理は大体 4x4行列を用いて行われる訳だが、

球面線形補間で角度を計算する時には、大体このクォータニオンに変換してから角度の計算したりする。

( というか検索すると大体これ使って処理してるから、それに倣うしか取り得る手段を持ち合わせていなかった

しかし ottuにはクォータニオンが分からぬ。

Wikipediaとか読んでも???である。

正直これに関しては圧倒的に知識が足りてない。

けど使えれば良いやっていうので気合で 4x4Matrix <-> Quaternion な関数作って回転の計算とかしてた。

これ書いてる間にまた興味出てきたから、何処かで時間取ってお勉強します。

https://github.com/ottu/ColladaLoader/blob/master/import/collada/import/collada/model.d#L1303


ぼちぼち満足した、もとい心が折れ始めた

IKの不充分な実装と、モデルを 360度くるりん回転させる時の球面線形補間のバグ

( 補間途中に回転角度が 360度以上 or 0度以下 になる状態が挟まると、一瞬だけ逆回転してしまう問題があるんです… )

により、実装が行き詰ってくる。

というか無職は「無限に金のなる木」を持っていなかった為、いい加減人並みの生活を取り戻す為に時間を使い始める。


やぁ、久し振りだね

書ける言語何がある?って質問に「Dと Delphi!」って答えたこの得体の知れない無職マンを雇入れてくれる心優しい職場に巡り合って数か月後、

社内で D言語を布教しようにもネタがねぇな…と思っていた時に思い出した ColladaLoader。

dubでコンパイル通らなくなってるんだが?

久し振りに開発環境整えてコンパイルしてみた所、何やら良くわからん感じになってた。

dub build した時にコンパイルが通らない…

ビルド用スクリプト作って手でオプションとかパスとか全部指定するとコンパイル通る。実行も出来る。

悲しみに暮れたが、どうせその内直るやろ~と思って開発再開する。

自作 xmlライブラリと自作行列計算ライブラリが憎い

久し振りにコード読んだら全く何をしてるのか分からなかった。

バグを修正するにも何処に原因があるのか判断付かなくなってた。

数か月間寝かせて発酵したバグはもう除去不可能な状態になっていると直感した。

kxmlと gl3n導入

いい加減定期的にメンテナンスされてるライブラリに置き換えようと決心し、dub package list眺めてたら良さそうなのを発見。

kxmlは文字通り xmlライブラリ。DOMでやりくりする感じ。

gl3nは OpenGLの関数と互換性を保てるように行列計算してくれるナイスなライブラリ。

4x4の Matrixは元より Quaterionへの変換も逆変換も乗算も実装してある。最高だった。

長くつらい戦いが再開した瞬間である。

ライブラリ変更完了したら…あれ…?

自作 xmlライブラリとか、xmlのパースから Colladaモデルの読み込み部分まで奥深くの所で使いまくってたし、

行列演算部分の修正とか正しい値なんて覚えてないしテストも殆ど書けてなかったからトライ&発狂の連続だった。

しかし明けない夜は無い、ライブラリの引っ越し作業にも終わりが来たのである。

嬉しさの余り満足してしまいそうになったのだけど、まぁまたミクさん眺めたいなぁの一心で実行出来る所まで体裁を整える。

さぁ、久し振りにあの可愛さを独り占めするんだ…と心ぴょんぴょんしながら実行した所…あれ…テクスチャ全く表示されないんだけど…

原因調べたらなんと、DevILの調子が何やら良くない様子。

この時点で DevILは殆ど更新がされておらず、その癖 brewで降ってくるヤツは





こんな事しないと使えない状態だったので、ええいままよと DerelictFI ( FreeImage ( http://freeimage.sourceforge.net/ ) ) も引っ越し作業をする事に。

this( Image image, string path = "" )

{
_self = image;
id = _self.id;
auto image_path = ( path ~ "/" ~ _self.initFrom ).toStringz;
FREE_IMAGE_FORMAT image_format = FreeImage_GetFileType( image_path, 0 );
FIBITMAP* image_original = FreeImage_Load( image_format, image_path );
FIBITMAP* image_converted = FreeImage_ConvertTo32Bits( image_original );
FreeImage_Unload( image_original );
_width = FreeImage_GetWidth( image_converted );
_height = FreeImage_GetHeight( image_converted );

GLubyte[] temp = new GLubyte[4 * _width * _height];
_texture = temp.ptr;
char* pixels = cast(char*)FreeImage_GetBits( image_converted );
FreeImage_Unload( image_converted );

//色情報の入れ替え。外すと色反転?
for( int i = 0; i < _width * _height; i++ ){
_texture[i*4+0]= pixels[i*4+2];
_texture[i*4+1]= pixels[i*4+1];
_texture[i*4+2]= pixels[i*4+0];
_texture[i*4+3]= pixels[i*4+3];
}

//テクスチャIDの初期化。setTextureの中に移動すると Effectの数だけ ID作られてしまうのでここから動かさない事。
glGenTextures( 1, &_textureID );

writefln("Image [%s] is loaded! width = %d, height = %d", _self.initFrom, _width, _height );
}

( https://github.com/ottu/ColladaLoader/blob/master/import/collada/import/collada/model.d#L493 )

画像処理周りって皆さん何使ってるんですかね? OpenGLとの親和性考えると DerelictFIで自分は満足してる感は正直あるけど…

行列演算、無事に移植出来てると思った?残念!動きませんでした!!!

覚悟はしていた。

あれだけ腐ったソースコードの消毒作業だ、一度で完治なんて無理無理と。

しかし思ってたよりアレだった。

うちのミクさん、全く踊れなくなっていたのである。

モデル自体の表示は出来る所までは戻って来れたのだけど、踊らそうとするとまぁ例外吐いて死んでしまう訳です。

おぉミクよ、死んでしまうとは情けない。


俺の戦いはこれからだ!

結局踊れなくなった原因は間違いなく行列演算部分にある訳だけど、

それを修正するにしても、もう殆ど .daeの中身の構造とか覚えていない訳です。

MMDfromColladaさん、今まで大変お世話になりました。

私、自分で .pmd/.pmx/.vmd 読み込めるライブラリ書きますね。

という事で、ColladaLoaderまたほっぽり出して、今は .pmx のバイナリと睨めっこしてます。

それの進捗もまぁ悪いのなんの。

永遠に終わる気配はない。でも楽しいから良いかなって。


最後に

だらだら書いてたら日付は変わっちゃってもうお昼間近だし、D言語のコードは全然乗ってないし、

圧倒的チラシの裏案件みたいになってしまってなんだかとってもごめんなさいって。

因みに今巷では MMD for Unity とか良いって聞きます。

ここまで読んで頂いたうえでこう言うのも何ですけど「MMDのモデルを使って何かを作りたい!」というモチベーションをお持ちなら

悪い事は言わないので本家 MMDなり MMD for Unity 使うなり、無駄な所に時間を費やすのは得策では無いと思います。

得に Unityなんて、業界的にはほぼ標準みたいな感じになりつつあるし、実際使い方さえ覚えてしまえば形あるモノを作るのには

とても便利なソフトウェアだと思ってます。

それに何より「お金に出来る技術」として身に着けるのも、大切な要因のひとつです。

私は 3D関係に関しては完全に日曜プログラマなので、偉そうな事も言えないのですが。

( Unityガチ勢方面から「結局行き着く所は直で OpenGLなり DirectX叩けなきゃどうにもならん時がある」という話は聞いた事ありますが、

低レイヤーになればなる程学習コストは高くなるだろうなぁって気持ちはあります。

ともあれ、こうして文章を書く機会とやる気をくれた D言語 Advent Calendarと投稿者の皆さん、そして少しでも目を通してくれた皆様に感謝を。

ありがとうございました!

( カメラの調整出来てなくて寸胴になってるミクさん )

スクリーンショット 2014-12-13 9.59.11.png