2010年2月3日
#
今の仕事で3D関連の事をやっています。
サンプルを動かすだけで度肝を抜かれることばっかりで…。
その中でも、かなりびっくりしたのがパララックスマッピングです。
下の画像を見てください。
なにやらお盆に変な立体が乗っかっているようですが、
実はこの立体部分は平らです。
この画像は、平らな円盤が表示されているだけで、立体に見えるのはただの模様(テクスチャ)です。
しかしながら、視線を動かすとその模様がリアルタイムに変わって立体があるかのように見える、
この技術がパララックスマッピングです。
ポリゴンのテクスチャとして、その部分がどれだけへこんでいるか、という情報を持ちます。
そして、へこんでいる量と視点に応じて、表示する点をちょっと動かしてやるわけです。
すると、へこんでいるように見えるよね、と…わかりにくいですね。
下の図は、真上から斜めになっているポリゴンを見た状態です。
ポリゴンに凹凸がないと左の図のように均等に張り付けられますが、
凹凸情報を計算して、右の図のようにテクスチャの張り方に変化をもたせると、より凹凸があるように見えます。
実際にはどうやっているか気になったのでソースを読んでみました。
ピクセルシェーダーのプログラムを読むのは初めてですが、概ねこんな感じじゃないかと。
黒い矢印は視線を表します。
本来のポリゴンであれば、赤い部分のど真ん中ですから、赤色で塗られる場所ですが、
高さマップを計算するとまだ視線はなにもぶつかっていません。
少しづつぶつかった部分からさらに視線を伸ばしていきます。
視線の深さがポリゴンの深さを超えるまですこしずつ計算し続けます。
実は視線の位置は赤と黄色の境目になる点でした。
そこで、最初にぶつかった点は境目の色で塗ります。
これをですね、ピクセル単位、つまり1ドット1ドット全てこの計算をするわけです。
何回の計算が必要なんだ!?と驚いてしまうのですが、
グラフィックカードはこういう計算を得意とするチップを積んでいます。
しかも、お互いが干渉しない=並列化可能なので同時に並列でぐわーっと計算されるわけです。
冒頭の図はこれにバンプマッピングと言う輝度を変化させる技術も使われています。
光の方向と垂直になっている面はより強く光るし、平行に近ければ暗くなります。
これも、テクスチャにそういう情報を持たせて、光り方を変えるわけです。
もちろん、このバンプマッピングもピクセル単位で計算されます。
これでレンガが積み重なった壁のような、小さな凹凸がある壁がリアルに見える、と言うわけです。
こんな感じですね。
ポリゴンで表現しちゃえばいいんじゃないの?とおもうんですが、
こんなのをポリゴンで表現すると頂点数がいっぱい必要になって速度が出ません。
最近のリアルな3Dにはこんな技術が隠されているわけです。
ところで、使った画像はDirectXSDKに付いているサンプルです。
気になった人はダウンロードしてデモを動かしてみましょう。
2010年1月19日
#
標準のrand関数は余り使わない方がいい、というのは知っているでしょうか。
これは線形合同法に起因する問題で、乱数といいつつも、結構規則正しいという特徴があります。
それを改良した乱数と言うとメルセンヌ・ツイスタが有名ですが、これもちょっとした問題があります。
それは、メモリの消費がかなり多い!
周期が2^19937 - 1 ということは、19937ビットのデータが必要なのです。
その大きさは約2.5KBですが、いくら高品質とはいえ、ちょっと量が多いですね。
それに、2^19937 - 1って数は6000桁以上!
宇宙の果てまで数えられる数の周期が必要な場面はそんなにないでしょう。
そこで、今回紹介するお手頃な乱数が、xorshift。
ソースは以下の通り。
unsigned long xor128(){
static unsigned long x=123456789,y=362436069,z=521288629,w=88675123;
unsigned long t;
t=(x^(x<<11));x=y;y=z;z=w; return( w=(w^(w>>19))^(t^(t>>8)) );
}
これだけのソースですが、2^128 - 1という周期をもつ乱数で、たった16バイトしか消費しません。
staticで確保しているx, y, z, wの4つの値を保存しておくだけで、前回の乱数の続きから生成できるため、
シリアライズ化するのも簡単というとてもお手軽かつ便利な乱数です。
なお、乱数の種(seed)ですが、x, y, z, wのすべてが0でなければどんな数でもいいです。
これは、128ビットの数値と見なすと、周期が2^128 - 1ということで、0以外の数値であれば、
循環する数列のどれかの要素になる、というわけです。
そういえば、メルセンヌ・ツイスタを改良したSFMTというものもあるみたいですね。
乱数は奥が深いです。
2010年1月6日
#
PSP開発読本を発送しましたが、一通だけ住所不明で戻ってきました。
心当たりがある人は左の「連絡をする」から連絡をお願いします。
#この記事は、連絡がつき次第削除します
2009年12月31日
#
冬コミ、まずは来ていただいた方にお礼を申し上げます。
お陰さまでなかなかの売れ行きでこれで年も越せそうという感じです。
さて、いろいろ問い合わせがあった完売した本のことです。
・PSP開発読本
まだ家には山盛り在庫があります。
これはとらのあなさんに委託しようと思ってます。
また、わんくま東京勉強会にも持って行きます。
・Lua開発読本
これは在庫がありませんし、印刷するなら夏コミかなぁという感じです。
正直、原稿での手直しをしたい場所は結構あるので…。
・ゲームの修羅場
こちらは、わんくま東京勉強会に持って行きます。
とらのあなへの委託はPSP本の感触次第で考えます。
残りの本。
在庫もないし、再度刷る予定もありません。
ここからは裏話。
とらのあなさんって以前委託を頼んだら取り扱ってくれなかったのですが、
受注委託という方法でなら取り扱うよ、と営業の方(と思ってたら社長でした、ドヒー)が
うちのスペースに来たので、ちょっと試してみることにします。
正直、5部送ってね、送料そっち持ちでみたいな事いわれそうで
赤字がでるだけなんじゃないかと思ってますが…どうなのよ、そこんとこ。
2009年12月9日
#
ということで、冬コミの情報です
「12/30(水) 東-V23a にじけん」になります。
出品物はこちら
・みんなで作るゲーム合同誌 ゲームの修羅場 B5 36p 300円
ゲーム合同誌の第2弾ですね。
諸般の事情からタイトルが変更になってますが、おんなじような感じです。
内容は以下の通り。
表紙は紫雨陽樹(PlatineDispositif)さんです。
[Interview]
D.N.A.Softwares:サーバ型ネットワーク対戦機能について コマスクドj
[Topics]
Subversionによるソース管理 出水 洸太郎(にじけん)
シューティングゲーム初心者向けのバランス調整法 サク(ASTRO PORT)
おもしろいゲームの作り方 isao(神奈川電子技術研究所)
二次創作文章系のためのサブライターのすすめ 柚坂みる(Blank Script)
クロスプラットフォームゲーム開発環境の構築とその運用 のりむし(AZN-WIND)
・Lua開発読本 B5 ??p 300円
夏コミで出せなかったLuaの本です。
オフィシャルが出ちゃったんで、正直必要なのか?という感じですが、
まぁ、好き勝手な事をいろいろ書こうかと思います。
あとは、夏コミの在庫とか持っていきます。
#売り子が私一人なので、だれか手伝ってくれる人いませんか?
2009年10月31日
#
今回はluaL_loadfile関数とluaL_dofile関数の違いです。(正式にはマクロだけど)
どちらも、ファイルからLuaスクリプトを読み込む関数なのですが、
luaL_loadfile関数は読みこむだけ、luaL_dofile関数は読みこんだ後スクリプトを実行をします。
Luaでこんなプログラムを読みこむ事を考えます。
function foo()
print "call foo"
end
function hoo()
print "call hoo"
end
C言語からfooやhooを呼び出しながら使う、という事を想定していますが、
ここで読みこむ時はluaL_dofile関数を使う必要があります。
C言語に慣れていると、luaL_loadfile関数で読みこむだけで中間コードへの変換は出来ているから、
実行する必要はない、と考えてしまうのですが、それは間違いです。
先ほどのスクリプトを書き換えるとこうなります。
foo = function()
print "call foo"
end
hoo = function()
print "call hoo"
end
以上でわかるとおり、グローバル変数のfoo, hoo に関数の内容を代入するために
スクリプト全体を実行する必要があるのです。
Luaには関数宣言に当たるものがないので、中間コードに変換されているだけでは意味を持ちません。
バッファから読み込む場合は、luaL_loadbuffer関数を使いますが、
luaL_dofile関数に当たる関数はないので、lua_pcall関数を使って実行する必要があります。
2009年10月14日
#
[数学]捻じれて縺れてこんがらかるの続き、計算編です。
まず、お互いの直線をベクトルと見なし、その外積を求めます。
この外積は何を意味するかというと、前回の平行な面における法線となっています。
この法線がわかれば、平行な面どうしの距離を計算する事が出ケイます。
それぞれの面から適当な点を1つづつ取り、bの面からaの面へ向かうベクトルを作ります。

この時、cと法線ベクトルの内積を求めれば、お互いの面の距離がわかります。
内積は、あるベクトル成分をどれだけ持っているか、という計算にも使えるわけです。
この辺のからくりがわからなくても、cと法線ベクトルが直角ならば、
aの面とbの面がぴったりくっついている、というのがわかればいいです。
ということで、こんな感じのソースになります。
int LineHitCheck(const Vec3 &a1, const Vec3 &a2, const Vec3 &b1, const Vec3 &b2){
Vec3 va = a1 - a2;
Vec3 vb = b1 - b2;
Vec3 nv = outer(va, vb);
Vec3 vc = a1 - b1;
if (nv.x == 0 && nv.y == 0 && nv.z == 0){
// 平行or同一の場合
Vec3 nv2 = outer(va, vc);
if (nv2.x == 0 && nv2.y == 0 && nv2.z == 0){
return 同一;
}else{
return 平行;
}
}else{
// 交差orねじれの場合
if (inner(vc, nv) == 0){
return 交差;
}else{
return ねじれ;
}
}
}
平行だったときの場合は飛ばしましたが、直線Aと直線Bが同一ということは、
直線B上にあるすべての点が直線A上にあるということです。
ですから、直線Aの一点と直線Bの一点を結ぶベクトルは、常に直線Aベクトルと0度or180度であり、
外積を求めると必ず零ベクトルになります。
ねじれの直線の距離は、法線ベクトルを正規化してcベクトルとの内積を求めると出ます。
カプセルの当たり判定で求める事があるかもしれません。
[数学]捻じれて縺れてこんがらかる の続きです。
今回は軽くおさらいです。
ベクトルを使うに当たり、三次元空間の内積、外積を解説します。
・内積
別名、スカラー積ともよばれ、ベクトルaとベクトルbの内積は一次元の数値となります。
要は、ベクトルじゃないよ、ってことです。
なお、内積が0になるときは、掛け合わせたベクトルが直交するときです。
これは二次元における内積と同じです。
なお、計算式は以下のようになります。
x、y、zそれぞれを掛け合わせてたします。
struct Vec3{
int x, y, z;
};
int inner(const Vec3 &a, const Vec3 &b){
return a.x * b.x + a.y * b.y + a.z * b.z;
}
・外積
別名、ベクトル積で、ベクトルaとベクトルbの内積はベクトルとなります。
この外積で出てくるベクトルはベクトルa、ベクトルbの両方に直交します。
前を表すベクトルと、上を表すベクトルから右を表すベクトルを作り出せます。
ここで、前と上を表すベクトルは必ずしも直行しなくてよいことが重要です。
なお、外積の結果が零ベクトルになることがありますが、
これは掛け合わせたベクトルが同一方向か逆方向を向いているときです。
先ほどの例だと、前を表すベクトルが真上を向いてしまうと、
北極点における東方向という感じで、右方向というのが決まらなくなってしまいます。
こういう場合に零ベクトルとなるわけです。
外積の計算式は以下の通りです。
x→y→z→x というローテーションを意識して、別々を掛けて引くというという形になります。
Vec3 outer(const Vec3 &a, const Vec3 &b){
Vec3 c;
c.x = a.y * b.z - a.z * b.y;
c.y = a.z * b.x - a.x * b.z;
c.z = a.x * b.y - a.y * b.x;
return c;
}
2009年10月9日
#
某会社の入社試験でこんな問題が出ました。
「線分A,Bが交差しているか調べる方法を書きなさい」
ははぁ、外積をつかってあーだこーだするんですな。
わんくま大阪勉強会で私がスピーカーをしたやつじゃないですか。
楽勝、楽勝…とはいかず、うだうだ書いてたら時間切れになってしまいました。
「ふふふ、簡単と思っているようだが…誰も二次元とは言ってない!!」
「な、なんだって!!三次元だと!!!」
ということで、線分は厳しいので、三次元空間で直線A,Bが交わるか調べる方法です。
その前に三次元空間における線の関係をおさらいしましょう。
線が2つあるとき、「同一」「平行」「交差」「ねじれ」のどれかになります。
「同一」は文字通りまったく同じ線です。
「平行」、「交差」は二次元でも出てくる関係なので問題ないでしょう。
残った「ねじれ」、これはなんでしょう。
一言で言うと、「平行」でも「交差」でもない状態です。
立方体の各辺において、図示しました。
ねじれの関係の辺は平行でもないし、交差もしてないことに注目してください。
で、どうすればいいのか。
直線同士が最も近づく距離を測ってやって、その距離が0なら交差、
そうでないならねじれ、ということにしましょう。
直線Aを含む平面を平面α、直線Bを含む平面を平面βと名付けます。
そして、平面α、平面βは平行です。
こうして、平行な平面αと平面βの距離を測ってやります。
なお、この平面α、平面βの組は必ず1つしかありません。
存在しなかったり、複数あったりすることはありません。
ということで、方針は決定したので
次回は実際のプログラムを書きつつ解説します。
数学好きの人は、実際にどんなプログラムを書けばいいか想像してください。
2009年10月7日
#
ニコニコで面白い動画があったので埋め込み実験を兼ねて貼ってみます。
文字通り、一時間でオセロを作る動画です。
Windowsをセットアップした直後のPCなので、最初から入っているソフトでなんとかしています。
参考になるようで、全く参考になりませんので注意。