2016年8月30日火曜日

Bitmapの高速アクセス

C#のBitmapでGetPixelとかSetPixelをやるとかなり遅いので、直接メモリアクセスするライブラリ。

本家ソースコードはこちら
NonSoft - Bitmap処理を高速化するサンプル(C#.NET)

ただしこのコードは24bppRgbアクセスのため、透過情報が破棄されてしまう。ということで32bppArgbに書き直している。ほとんどコピペだけど。

あと色情報をbyte配列で受け取るメソッドを追加してある(与えるメソッドは無い)。

このクラスを使用する場合はSystemとSystem.Drawingの名前空間が必要です。

2016年8月23日火曜日

れんだりんぐえんじん(あるいはXファイルパーサ)



やっとXファイルを適切に読めるようになってきました。といっても内部処理が変わるだけで、結果には何も影響を与えない(与えてはいけない)ので、見た目に変化はありませんが。あ、背景色は変わってますが。

中途半端なXファイルのパーサを作るだけに丸4日くらいかかったのかな。さすがに掛かり過ぎじゃないかとorz

今回作ったものはテンプレートもXファイル内のモノを利用できるので、ちゃんとしたXファイルなら意味を知らないテンプレートが出てきてもちゃんと読めると思います。最も、それを利用するためのコードにはメンバ名等を適切に設定する必要がありますから、あんまり意味ないんですけども。

今回はパーサを作るのが初めてということもあって、かなり妥協してぐだぐだになってるので、もうちょっと綺麗に処理したいなーとか思う反面、もう触りたくないとも思ったり。


段々とPCソフト作りは飽きてきたので組み込みに戻ろうかなとか考えたり。そもそも簡易3D表示はIMUの動作確認用に作り始めた気が。でもマイコンの方もかなり苦労することになると思うので、ちょっと手が出ないです。気力が足りない。


後何日かしたらPCとも組み込みとも別件の部品がいくつか届くはずなので、まずはソレを優先かな。そっちもそっちで苦行の道っぽいけど。

2016年8月22日月曜日

型を指定するHashtable

C#にはHashtable(連想配列)があって、コレを使い時があるけど、HashtableはKeyもValueもObject型なので使い勝手が悪い。ということでKeyとValueの型をそれぞれ指定できるラッパーを作ってみた。
一部のメソッド等は実装しただけで動作確認していないので動かなかったらごめんなさい。例外とかもちゃんと作ってないです。
連想配列とか使いたくなるとJSとか便利だなーとか思ったり。

追記:2016/08/29
.NetFW4以降ならConcurrentDictionaryを使いましょう。こいつならKetとValueの型を指定し、スレッドセーフでアクセスすることができます。

2016年8月20日土曜日

(小ネタ)NEW GAME#7

*モデルガン・エアガン
FN Five-seveN
H&K UMP
スプリングフィールド M14
ベネリ M4
-拳銃数種


*細々
12ゲージ?ショットシェル(発砲済み)
AMMO BOX
迷彩布
-拳銃型水鉄砲
-その他いろいろ

ところで発砲済みの空薬莢はamazonで売ってたり、沖縄の放出品ショップで買うことができるが、例外的に12ゲージのショットシェルは山で拾うことができるらしい。ウチの親が拾ってきた。狩猟で使ったのが落ちてるらしい。まぁ広い山の中でエンカウントするのは相当にリアルラックが必要だろうが。それでも、競技射撃にも使用されるから、入手性はダントツかも。


BB弾小袋の自販機イイね。0.2g1600発600円、0.25g950発600円、0.2g蓄光1000発1200円、位で売って。でもコイン選別的には500円1枚でやれると楽なんだよな、前者2個は1000円、蓄光は1500円でどうだろう。暴利だな。


New Gameは魅力的なキャラが多いけど、褐色さんはかなり好み。

2016年8月19日金曜日

れんだりんぐえんじん(Xファイルを読む)

「れんだりんぐえんじん」って、「えれくとりっく・えんじぇぅ」っぽい語感ですよね。カタカナをひらがなで書いただけですけど。

さて、表題の通り、Xファイルを読み込むようにしてみました。

ゲームプログラマーを目指すひと Xファイルを解析してみる
Xファイルの読み込みは↑のブログを参考にしました。分かりやすかったです。

今回は頂点とマテリアル、法線だけしか使わないので、てきとーにやっつけ読み込みです。まぁメタセコイアから出てくるXファイルってほぼ頂点、マテリアル、法線だけですけども。

今回サンプルデータを作るに当たり久しぶりにメタセコイアを使いました。と言っても新しくなった方ではなく、古い方(Ver3)ですが。
Ver3は昔触ったことがあった事、Ver4でXファイルを出力するにはライセンスを購入する必要が有ること、等が理由です。



とりあえずキューブを表示してみました。
Xファイルでは面に色を指定できるので、ソレで色をつけています。なんか途中で間違ってる気もしますが、とりあえずそれっぽい色になってるのでいいかなと思います。

それと法線を使用して、手前を向いているポリゴン以外は表示しないようにしました。これにより奥側の面は非表示になりますから、深度を使った判定は必要なくなります。もっとも、こちらを向いているポリゴンが2枚以上重なった場合の動作はよろしくないですが、単純なキューブやスフィアなら問題ないと思うのでこの方式で行こうと思います。


単純ではないモデル、例えばキューブが2個並んでいるような形状の場合は以下のようになります。





上の画像は正しい状態ですが、下の画像は反対から見た図で、本来は奥にあるモデルが後に描画され、手前側に表示されてしまっています。
2個別々のモデルでなくても、単純なキューブにちょっとした突起を作っただけでも同様の問題が発生します。ポリゴン同士の組み合わせで「谷折り」となる部分があると、その辺りで問題が発生します。


モデルの色については、現在のところ1面辺り1色としていますが、Graphicsのグラデーションをうまく使えば頂点カラーのような機能も作れるかもしれません。そうすればもうちょっと表現力が上がるかも。どっちにしろ箱や球しか表示できないわけですが。
それでもポリゴンを適切に割って色をつければ文字等も表示できますから、IMUの動作確認に箱を1個表示し、軸がわかりやすいように文字を表示する、くらいはできるかもしれません。
もうちょっと発展の余地有りという感じです。

2016年8月18日木曜日

C#でレンダリング(深さ情報を扱う)




上が前回のレンダリング結果、下が今回のレンダリング結果。
データは同じだが、見た目的には黒の輪郭線の太さを1から3へ太くしたこと、1面毎に色を設定したことが違いとなる。

表示結果は似たようなものだが、プログラムとしては全く別物となってしまった。

まずBitmapの使用を増やしたこと。
前回はBitmap1枚にどんどん書き加えていたが、今回は出力バッファの他にDepthを管理するための画像や、現在のポリゴンのレンダリングバッファと、その深さを描くためのバッファを用意した。
前者2枚はレンダリング開始から終了まで常に開きっぱなしだが、後者2枚についてはポリゴン1個毎に動的に生成している。

深度管理の画像は予めColorと深さ値(今回はintを使用し24bitで管理)を相互に変換する関数を作成しておく。次に出力Depthを最遠でクリアしておく。
ポリゴンを書く場合は画像1枚にポリゴンの画像を、もう1枚にDepth値を書き込む。
その後でDepth値と深さ値画像を比較し、Depth値が深い場合はアルファを0にして透過させ、すべてのピクセルでこの処理を行った後に、バッファとDepthをそれぞれDrawImageで出力バッファに書き加える、という処理になる。

Depth値は1ピクセル毎に計算しているのではなく、ポリゴンの各頂点の平均値を使用している。そのため1ピクセルごとの位置を計算する手間は必要ないが、それでも上の画像を作るだけでかなり時間がかかる(0.8秒程)。
一般的なレンダリングエンジンではGPUの数千コアで並列処理するが、CPUの1コアだけで処理しているために非常に時間が掛かる。

400x400ピクセルの画像で、6面を表示し、辺も含めると12枚の画像となる。それぞれレンダリングとDepthがあるから、24枚の画像をforネストで操作していることになる。それでも3.84Mピクセル程度なので2000x2000ピクセルの画像1枚分程度なのだけど。



このくらいの処理であればCUDAで書くのはそんなに大変ではない気もする。ウチのグラボはGeF 970なので、1000コアくらいで並列処理できるのかな。
でもそこまでやるならレンダリングエンジン探してこいよ、という話になってしまう。

とりあえず、平行投影とはいえ前後に重なったポリゴンを適切に表示できるモノを作っただけでも満足かな。

C#でれんだりんぐえんじん



ちょっとFormで3Dを表示したくなったので試してみた。
とりあえずXNAのVectorとかQuaternionを使えば簡単に座標変換ができるので、平行投影ならサクッと作れる(それでも結構なソースになるが)。

ただ、あくまで座標変換して書き重ねてるだけなので、後ろにある物体を書いても、前にある物体の手前に書かれることになる。

ちゃんとした表示を行うなら遠近法の変換だったり、前後を比較して書き込む順番を変えたりといった作業が必要になる。そこまでやると3Dのレンダリングエンジンになってしまうし、かなり面倒なことになると思うけども。

それから、この座標変換は1点のみに適用される。なので線や面に対しては適用されないことに注意する必要がある。
例えばGraphicsに四角形を書く場合、通常であればFillRectangleとかを使うことになると思うが、コレでは思い通りの結果にはならないと思う。サンプルの通り、頂点の位置を変換してからFillPolygonで表示すれば予想通りの結果となる。


他の方法としてはGraphicsのTransformを使う方法もあるが、コレは思ったように動かすのはちょっと難しい。ということで僕は早々に見切りをつけてXNAの型を使っている。
ただしXNAを使ったところで線やポリゴンを書くことが限界だから、テクスチャを貼りたいならGraphics.Transformを使う必要があると思う。


ちゃんと3Dを表示させたい場合、最低でも前後の位置関係はどうにかしたい。コレはデータを一旦バッファにためてから、頂点の平均位置を比較して視点から遠い順に書いていく、という感じの処理になるのかな。
とりあえず当初の目標はすでにクリアしているのだけど、もうちょっとレンダリングエンジンについて調べてみると面白いのかも。