Hatena::ブログ(Diary)

雷鳴の日記

2017-01-29

3D関連理論と2D平面に「立方体」を描く

前の会社はPS4XBOXなどのコンソールゲームを開発する仕事をやってた。

今の仕事はゲーム開発と関連ありませんが、只今春節休み中、特にやることなくて詰まらない。3D関連の理論を復習してあれそれ実験をやってみる。

目標は2D絵の関数で立体に見える立方体を描く。

簡単に理解できるため、マトリクス演算などわりと理解しにくい知識は使わなくてベクトル演算だけ使う。

まず基礎理論を紹介する。2次元直交座標系は、2次元ベクトルは略。使うのは右でXが正、上でYが正。3次元直交座標系は右手系を使う:X軸の方向は右手の指を沿って、Y軸は指先の方向に示すとき、親指の方向はZ軸にする。

立方体の出番だ。立方体の中心は(0, 0, 1)に置く。辺の長さは2なので、頂点は (1/-1, 1/-1, 2/0) 全部八つだ。

f:id:sorayukinoyume:20170129224236j:image

blenderからスクリーンショットを借りる)

六つの面にも「正面」と「反面」がある。正面から観測して見えるが、反面から見れば見えないはず。例えば上の画像に y=1 の面は見えない。

正面はどこへ向くのは易く判断のため、何かのルールがあれば便利になる。このルールは、「頂点を右回り順番で描く表面は正面」だ。原因は、右手座標系でそうすると頂点を連結するベクトルのクロス積の方向は描く表面の正面方向になる。

だから画像の中の立方体の表面で、頂点の連接順番はこうだ。

f:id:sorayukinoyume:20170129224237j:image

f:id:sorayukinoyume:20170129224238j:image

頂点は右回りで連接する(見える)表面は三つだ。ほかの表面は左回りで連接する(こっちに向いてない)から見えない。

向こうから観測すれば、頂点の連接順番は逆になる。

次の問題はどうすれば2Dのスクリーンで3Dの立方体を表示できる。

現実の立方体を見える原因は、立方体から人の目まで光線がある。そして目から「スクリーンの中の」立方体まで連接する「光線」は決してスクリーンを通過する。スクリーンはただの平面なので、その「光線」はスクリーンと交わす点で光線の色を描いたら2D平面で立体感ある立方体を表示できる。

ならば、その「交わす点」の計算方法を知ったらスクリーンに表示すべき画像は手に入れる。

三角形相似の性質で交わす点を計算できる。

f:id:sorayukinoyume:20170129224239j:image

A点(既知)は目で、D点(既知)はスクリーンを通過して見た点。BC点はスクリーン平面にある。AC⊥BC。

ACの長さも既知なので、|AE|=(ベクトルAD)・(ベクトルAC)÷|AC|。←投影

だから|AB|=|AD|÷|AE|×|AC|。

ベクトルABの長さはした後、(ベクトルAB)=(ベクトルAD)÷|AD|×|AB|。

A点もベクトルABも既知になったらB点の座標も知った。

スクリーン平面で2D座標系を作って、B点の座標はスクリーンの座標系に変換(スクリーンに投影)したら2D平面で表示できるようになった。

ソースコード(結果も見える)

https://github.com/sorayuki/Learn3D (learn3d-1.html)


コードは解説して、

3dutil.jsの中にベクトル演算のクラスがある。

「カメラ」は「目」だ。カメラのデータはカメラの位置、方向、スクリーン(projection planar)への距離を含む。立方体の頂点の座標はスクリーン座標に変換できるメソッドも含む。

スクリーンの座標系は、Z軸はカメラの方向と反対する単位ベクトルk(←カメラ方向は見える表面の方向と反対だから)。そしてZ軸と垂直する任意の単位ベクトルiをX軸方向にして、Y軸の方向はZ軸方向のベクトルとX軸方向のベクトルのクロス積jだ(そうしてXとYのクロス積はZ軸の方向)。上の画像に、ベクトルABをスクリーンに投影するとき、座標は (AB・i,AB・j) になる。

DrawRect関数立方体の表面を描く。正面はカメラ方向に向く表面(頂点は右回り順番で描く表面)だけ描く。

p.s.

なんだか長い時間で日本語で何も書いてなくて忘れそうになった。ツイッターもほとんどあきらめた。復帰したいけど、タイムラインに入れなかった気が多少とも感じる。

この記事を書くとき、学んでなかった専門用語は多くて難しいと思う。辞書とかネット検索エンジンとかのツールを活用して何とかなる。自分以外の人も理解る可能性があるかもって思って…

とりあえずこれからもよろしくね

2016-04-09 デレステ譜面フィルター

デレステ譜面フィルター

久しさ記事を書いた。

最近デレステを遊んだ。私は大学生のときから音ゲーに興味が起こる。

そしてネット友はデレステスクリーンショットを見てくれたあと、始めた。

ユビート遊んだとき、Jubeat Analyzerというツールがあるが、デレステにそんなツールはない。

じゃあ自分でつくろうって思って、QTでこのツールを書く(QTアンドロイド端末でも動くと聞いた、これであとは遊び機能も付けるかも)。

譜面スクリプトスクリプト読み込み、拍の計算、ノートの表示

これ全部実装した後、大プロブレムにあった:フレーム制御の方法はわからない。

いろいろ試した後、しばらく放棄した。どう調整してもラグ。あとはStackOverflowで聞くつもりだ。

そしてこのコードはリアルタイムを要求しない場合で使えるかも、と思ってビデオフィルターを作った。

f:id:sorayukinoyume:20160409002934j:image

いつか時間があったらまたリアルタイム再生の方法は勉強する。しばらくこのままで

AviUtlフィルター、ソースコートと夢色ハーモニーのMaster+譜面はここにアップロードした

http://www1.axfc.net/u/3648917

AviUtlでファイル→インポート→CGSS Fumen Overlayで譜面スクリプトを読み込める

2016-01-26 lstrcmpA ≠ strcmp

多分中二病発症とか何とか、とりあえず理由は忘れた。

何年前に私は書いたインポートライブラリ作成ツールに、文字列の比較はC言語のstrcmpの代わりにWindows APIのlstrcmpAを使った。

VC用の.libファイルに、二番目のメンバーはこのライブラリーに全部使われるシンボルの集合だ。シンボルに要求は「lexical」順に並べる。*1

それは簡単なアルゴリズムだね:ソート。

ソートの時文字列の比較は必要だ。あの時は私はstrcmpの代わりにlstrcmpAを使った。多分「Windowsから提供のAPIを使ってみたい」だろう、もうわかんない><

あのツールは今夜使った時、使えない.libを作成した。具体的に、linker.exeは時々「LNK2001:外部シンボルは未解決です」とリンクエラーになった。「一部のシンボル」で、「全部」じゃなかった。例えばimpSは使えるがSは使えない。

それは本当におかしい。.libファイルをバイナリ編集ツールで検索したら、シンボルテーブルに確かにそのシンボルがある。

dumpbinツール使って、私のツールで作成した.libファイルの詳しい情報を調べて、不具合の所は発見した

f:id:sorayukinoyume:20160127003531j:image

確かに大文字の「S」は小文字の「i」より値が小さいはず?しかもNULL_THUNK_DATAの最初の文字は 0x7f なので、その場所に置くのは決して正しくない。

コードにブレークポイント使って確認して、意外なことは発見した

lstrcmpA("impS", "S")

このexpressionの戻り値は < 0

このlstrcmpAをstrcmpにしていろいろ試したら、今まで問題なし

f:id:sorayukinoyume:20160127004435j:image

MSDNでlstrcmpの説明を調べて、

The function calls CompareString, using the current thread locale, and subtracts 2 from the result, to maintain the C run-time conventions for comparing strings. *2

確かに普通のstrcmpではない。

でもこのコードの書いたとき、どうしてlstrcmpAを使ってたのだろう・・・

インポートライブラリ作成のツールソースコードは、整理終わったらgithubにupするつもり。ちょっと汚いけど

*1The second linker member includes symbol names in lexical order, which enables faster searching by name. http://download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/pecoff.doc

*2http://msdn.microsoft.com/EN-US/library/ms647488(v=VS.85,d=hv.2).aspx

2016-01-25 フィボナッチ数の計算

久しぶり、雷鳴です。一昨年7月から社会人になった。いろいろ忙しい(たぶん)ので記事を全然書いてないorz

アルゴリズムに上手なプログラマーはわりに人気のようだ。でも自分はアルゴリズムに下手なんだ。

だからすこしアルゴリズム技が進めばいいと思って、日曜にMITの授業録画(の中国語訳バージョン)を見て勉強した。

その中で新しいフィボナッチ数の計算方法ははじめて知った:

行列

[ 1 1 ]
[ 1 0 ]

を累乗して、その左上の数は「累乗の回数」番目のフィボナッチ数

累乗も、そのまま a * a * a * a * ... より早い方法がある。

行列[(1 1) (1 0)]はAにして、累乗の回数はnにする。

そしてnはバイナリ表示の方法で、n = b0 * 2^0 + b1 * 2^1 + b2 * 2^2 + b3 * 2^3 + ... にする。

(そのなかで b0, b1, b2, b3...∈{ 0, 1 })

だから A^n = A^(b1 * 2^0) * A^(b2 * 2^1) * A^(b3 * 2^2) * ...

b[x]=0の時、A^(b[x] * 2^x) = A^0 = I

また任意行列Bは IB = B、BI = B だ。

だからこんなループ

R ← I
An ← A
while n ≠ 0
    b = n mod 2
    if b == 1 then
        R ← R * An
    An ← An * An
    n ← (n - b) / 2

は、Aのn回累乗を計算できる。

さっそくpythonでそんなプログラムを書いてみようと、

https://github.com/sorayuki/practise/blob/master/Fibonacci.py

こうなった。

この方法で、第20000番目のフィボナッチ数は、乗法160回と加法160回でゲットした

2014-02-03

暗黙的リンクのライブラリ編集ツール2

一昨日公開したツールで、

Noprefixの意味を誤解した。

Prefix正規表現で言えば 「[\?\@_]*」 ではなく「[\?\@_]」です。

だからプログラムをまた更新した。

再うpして一昨日の記事にもダウンロードリンクを更新した