スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

光の減衰とかとか

光の減衰を調べていました。

そうそうシェーダでよく
sampler ShadowMapSmp = sampler_state
{
Texture = ;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};

などしてやって
えふぇくと . ぱらめーた ["てくすちゃ"] . セtバリュー(てくすちゃ!);
で渡したりしてる人もいるかもしれませんが、

sampler DeviceTextures : register(s0);
と書いておいて、
GraphicsDevice.Textures[0]=てくすちゃ
としてやることもできます。
多分少し早いです。



本題の光の減衰について。
<ケプラーの逆2乗の法則>という偉そうな名前のものがあるそうな。
以下がそう
E=I/r^2

Eは出したい点の光の強さ(照度)
Iは光源の光の強さ(光度)
rは光源から点の距離


まんま使えそうですね。
ソースってほどのものにもならなそうなのでこれでおしまい。

同時に色がどう届くのか?についても調べました。
・紫は波長が短く夕焼けのように空気の層が厚くなると届かない。
・赤は逆に強い。
・色とはそもそも波長の吸収率の違いによって生まれた概念だ。
調べているとドンドン知らなかったことが分かってきてネット普及に感謝しました。
同時に距離による色の届く差を作ってやりリアルにしてみました。

結果的にはちょこっと計算いれるわりにそこまでガツン!と綺麗になるわけではないです・・・
MAPによってその波長を変える~とかするとシーンによって表情が変えれるかもしれないのでもうすこし弄ってみようと思います。

ねよっと。
スポンサーサイト

全方位シャドウマップ

全方位シャドウマップ!
5日ほど前からの怒涛の流れはこの記事を書くためのものだったりします。

そしてもってこの機能を実装したのは「前からずーーっと言っているあるプロジェクト」のためのものだったりします。まだひっぱっています。(まだ出来ていないんじゃ?って香りを嗅ぎ分けたあなた!スルーですよ)

ってことでサックリ実装したかったのですが、なかなか大変でした。
機能としては「キューブマップを利用したシャドウマップ」です。
点光源が使えますね。なんなく出来るとおもったのですが詰まりました。

というのもまずこれ
cube8.jpg
以前紹介したこの記事の絵にレンズを加えます。

このレンズってのはただのイメージで所謂「プロジェクション行列」です。
キューブマップは色をもらうときにベクトルが必要というのは昨日話した内容です。
このベクトルってのはシャドウマップならば光からAの位置を指しますね。
ところがこのベクトルと実際書かれたキューブマップの間にプロジェクション行列を挟んでいます。

どういう関係になっているのかわかっていないと実装できないですよね・・・
しかーも!これまた資料が皆無。
ため息ついて実験したわけです。

結論:視野角90度なら歪みが0です。

そりゃ予想はできますよ?でもあとからこのズレの知識がないのに実装していて影響が別のところにでないとも限りません。
試す作業が大変だったのです。

まず
目の前にシャドウキューブマップ(の1面)を置きベクトルを作ることから始めました。
任意の6面のうち一つを選ぶにはこれまたキューブマップを使いました。
6方向に0,127(128),255で色分けしたキューブマップをエフェクトに読み込ませておきます。
そんでもって「書き込み点のワールドpos-LightPosition」をtexCUBEに渡してやりどの面のキューブマップを使っているか?とUPの方向などを持ってきます。

これでEyeとAtとUpが分かるので昔書いたように現在の書いている点が使用したであろうView行列がわかります

float4 uv = mul(mul(mul(pos,World),LightView),LightProjection);
float4 LightCubeVec = float4(uv.x/uv.w , uv.y/uv.w ,-1,1);


これで目の前に-1~1にぴったりのキューブマップの一面が展開された状態で、その点がXとYのどこにあるかを示してくれています。
Z座標はとりあえず-1として原点からキューブに伸びたベクトルを作ります。

これだけでは目の前にある面がキューブの真上のものかもしれないので、とりあえずその面がどの面をむいているのか調べてベクトルに回転を加えます。

ここで少し詰まりました。
というのも上記で調べたUpベクトル(どこが上か?を指すベクトル)などからCosとSinを作って回転させようと思ったのですが、Cos(0)=1、90や-90は0、180は-1などの計算をさせるとところどころ近似値になって出てきます。さすがにこういう節目っぽい角度はぴったり来るだろうとたかをくくっていたのが失敗でした。
floatに対する小数点の制限?などが絡んでこのようになるのでしょうが(全く調べてないので別問題かもしれません)とりあえずこれを解決しなければなりません。

ただの調査であり、常に使用するならもっといい方法を考えようと思い、こうしました。
以下は使用しないほうがいいです。上下の面の生成の仕方がキューブマップの理解が乏しかったためにこのようなことが起きています。
本来そのまま上を向くべきなのですが、すべての面が左右逆になってほしい(他の実験も兼ねていた)ので少し仕様が理解しにくいかも。

VecCubeはキューブ面の向いているベクトル
Upはその上方向を表すベクトル

float c = -VecCube.z + (-Up.z * Up.z);
float s = VecCube.x * Up.y;
float4x4 RotateY = float4x4(c, 0, s, 0,
0, 1, 0, 0,
-s, 0, c, 0,
0, 0, 0, 1);
c = Up.y;
s = -Up.z;
float4x4 RotateX = float4x4(1, 0, 0, 0,
0, c, -s, 0,
0, s, c, 0,
0, 0, 0, 1);
LightCubeVec =mul(mul(LightCubeVec,RotateY),RotateX);

これで一応必要な実験ベクトルがでましたね。

あとはこのベクトルをtexCUBEに渡して得られた色描写点の位置-光源を渡して得られる色の相違を調べるだけです。

結果
cube2.jpg
取得した色を比べて違うところだけ黒になるようにしました。
プロジェクションは
Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(90), 1, 0.1f, 1000.0f);
で試しています。
MathHelper.PiOver2としてあっても同じ90度を指します。

っは!ここまで撮ったのに比較の45度とかの絵を撮影するの忘れてた!
立ち上げるの面倒なので察してください。
6面の正面に当たるところは綺麗ですが、角度が広がると一定角度から真っ黒になります。

90度でもよーくみると少し取得位置がずれているところが見受けられます。
理由は。。。。。。
なにかしら問題があったのでしょう。(遠い目)



さて。キューブマップに対してそのままベクトルを入れてやってもOKと分かればこっちのものです。
といってもパースティクティブキューブシャドウにするなら↑を参考に作り変えてください。

シャドウマップの鍵である深度の受け渡しがもうできそう!ってとこで、昨日の記事にもあったように面の方向がネックになってきます。
ライトからみた方向をその方向のキューブ面に張った場合
・ポジティブネイティブのXとZ面は左右が逆になる
・Y面はどちらも上下反転になる

結構問題があります。

解決策は色々あると思います。当初、
上下だけUp方向をいじってやり、どの方向も左右逆にはってやり取り出すときに反転させる。
という方法を使っていましたが。
キューブ面を生成するときに渡すView行列を変えてやり、すべての面が上下左右逆に撮影したうえで、張る面を逆(上面と下面の入れ替え)にしてやる→取り出すベクトルVを-Vで取り出す。
としました


POS-ライトのベクトルからキューブマップの方向を取り出す時、昨日の記事にもかぶるのですがもう一個のキューブマップを使いました。
round(2.0f*texCUBE(VecCubeSmp,-V)-1.0f)
などとしてやっているのですが、roundが気になりますよね。
色が
0=0
255=1
127か128=0に近いけど違う

という結果になります。
欲しいのは-1~1には変わりないのですが、0でないと困るので仕方なしのroundです。
もっといい方法があると思うのですが思いつくまではこれで我慢っと。


や、やばい。無駄に長い記事が今日もできてる・・・・

ねよっと

キューブマップとの格闘

毎度(というか毎日)知らない言葉がでてきて勉強しては実装しているのですが、キューブマップは思ったより大変でした。

キューブマップのサンプルはMicrosoftから出ているSkyなんちゃらってのがあるので実行してみてください。
Xboxのコントローラーで動くようにできてるので
this.keystate = Keyboard.GetState();

if (keystate.IsKeyDown(Keys.D1))
{
myCamera.Target = new Vector3(0, 0, 0);
myCamera.Distance = -50f;

}
if (keystate.IsKeyDown(Keys.D2))
{
myCamera.OrbitUp(0.01f);
}
if (keystate.IsKeyDown(Keys.D3))
{
myCamera.OrbitRight(0.01f);
}

を加えたら動きました。
ほほ~全6方向を画像で覆ってやりどこを見ても綺麗に繋がって見えるわけですね。

んでこの辺まで進むと、いざ自分のプログラムに付け加えるぞ~ってところで止まりました。
というのも他のサンプルが少ないんです。
死活問題です。

では説明。ちなみに長いので、時間の無い方は遠慮したほうがいいかもです。(汗)

まずDirectXのHELPでやられました。
あやつ「左手系座標」で説明してやがります。
こっち(XNA)は右手です。

ってことで仕様から。方向とかも
資料が無い。
た、た、試すか・・・・・・・・・(泣)

まっず絶対わすれちゃいけないのがこれ
cube6.jpg

まず方向を見るためにエフェクトにDirectXViewerで作成した6方向の名前とテクスチャ方向をいれた図をぶち込みまして表示
cube.jpg
こんな感じででてきます。
cube3.jpg
DirectXの説明からこのようなイメージで頭に焼き付いている人もいるとおもいますが
こうのほうがいいです
cube4.jpg
んでもってこうなります
cube5.jpg
うわ。下手な絵ですいません。
伝わればいいんです。

PX=ぽじてぃぶえっくす
NX=ねがてぃぶえっくす

ですね。

次にこの貼り付けた画像の取り出す方法です。
結論からいうと「中心からのベクトル(ノーマライズいらない)を与えるとその先にある色を取得してくれる命令を利用する」です。
結構重要なのに詳しい説明はどこもしてくれていないです。常識でわかれ!っていうかもしれませんが初心者にはつらいところ。

texCUBE(s, t)
3D のキューブ テクスチャ参照。s はサンプラまたは samplerCUBE オブジェクト。t は 3D テクスチャ座標。

texCUBE(s, t, ddx, ddy)
微分を指定した、3D のキューブ テクスチャ参照。s はサンプラまたは samplerCUBE オブジェクト。t、ddx、ddy は 3D ベクトル。

texCUBEproj(s, t)
3D 射影のキューブ テクスチャ参照。s はサンプラまたは samplerCUBE オブジェクト。t は 4D ベクトル。t は、参照が実行される直前の成分で除算されます。

texCUBEbias(s, t)
3D のバイアス キューブ テクスチャ参照。s はサンプラまたは samplerCUBE オブジェクト。t は 4D ベクトル。参照を実行する前に、ミップ レベルに t.w のバイアスがかけられます。


てな感じの命令が用意されています。もちろんこれはシェーダでのお話。


んでんで最初の貼り付けた絵は球の法線をtexCUBEに渡しています
そうなるとベクトルっていっても角っこを指定したらどうなるの?って思う人がいそうですね。
実験しました。
例えばXYZすべてが-1~1の立方体があるとして
(1,1,0)とかだと丁度キューブの角を向いたベクトルになりますね?
このベクトルで色を欲しがるとどっちの面の色を持ってくるか?って問題です。
答えはCubeMapFaceのナンバーが若い方が強いみたいです。
PX、NX、PY、NY、PZ、NZの順ですね。
全箇所試したわけではないのであしからず!


CubeMapFaceといえばこれもやられました。
6枚もあるのでforを使って(CubeMapFace)iとしてフェイスを切り替えしていたんですが、ブレイクをかけてこいつにポイントして+を開くとNX~Z、PX~Zの順に表示されます。
これはiの表示順だと勘違いしちゃったんですね。。。違うのであしからず。
PX、NX、PY、NY、PZ、NZが正しいです。

話がそれましたが、次に思うことは「んじゃキューブマップつなげてデバッグ表示してみよう!」となりますがこれまた難儀。
ソース載せます。
注意:この方法は「重くていいからソースが短く後でサックリ消せる」ことを重視していますので、とにかく重いです。古いPCの人はまともに見れないかもしれません。
もしデバッグでも軽いものがいいという人はこの方法の次に書いた方法をお勧めします。

とりあえず関数としてどこかに
        private void DebugCubeMap(RenderTargetCube r, int CubeMapSize, int CubeMiniMapSize)
        {
            //動的キューブマップ簡易目視用
            int i, cubeX = 0, cubeY = 0;
            byte[] data = new byte[CubeMapSize * CubeMapSize * 4];
            for (i = 0; i < 6; i++)
            {
                r.GetTexture().GetData((CubeMapFace)i, data);
                Texture2D LookTex = new Texture2D(GraphicsDevice, CubeMapSize, CubeMapSize, 1, TextureUsage.None, SurfaceFormat.Single);//カラーMAPの人は最後を.Colorに。
                LookTex.SetData(data);
                this.spriteBatch.Begin(SpriteBlendMode.None);
                switch (i)
                {
                    case 0:
                        cubeX = CubeMiniMapSize * 2;
                        cubeY = CubeMiniMapSize;
                        break;
                    case 1:
                        cubeX = 0;
                        cubeY = CubeMiniMapSize;
                        break;
                    case 2:
                        cubeX = CubeMiniMapSize;
                        cubeY = 0;
                        break;
                    case 3:
                        cubeX = CubeMiniMapSize;
                        cubeY = CubeMiniMapSize * 2;
                        break;
                    case 4:
                        cubeX = CubeMiniMapSize * 3;
                        cubeY = CubeMiniMapSize;
                        break;
                    case 5:
                        cubeX = CubeMiniMapSize;
                        cubeY = CubeMiniMapSize;
                        break;
                }
                spriteBatch.Draw(LookTex,
                    new Rectangle(cubeX, cubeY, CubeMiniMapSize, CubeMiniMapSize),
                    new Rectangle(0, 0, CubeMapSize, CubeMapSize),
                    Color.White);
                this.spriteBatch.End();
            }
        }

を入れてやり
DebugCubeMap(cubeRt,1024,125);//デバッグ用キューブマップの表示
で表示します
引数は「CubeMapの元サイズ」「目視するサイズ(1辺の大きさ)」を渡して下さい。
コピペの際、タブが入らなかったので半角スペースを全角に置き換えて貼り付けました。
テキストエディタなのでCtrl+Rなどの置き換え機能を使用して全角スペースを殺してください。

↑の方法は重いので気にいらない!という人は以前紹介したMSDNのBBSの中間くらいの記事を見てください。



これでやっとキューブマップの全容が見えてきましたね。
感のいい人ならここで気になることも出てきたはずです。
「最初からテクスチャを用意するなら楽だけど動的に生成した場合、左右逆にならない?」ってことです。
キューブマップの使い方は「反射するものを綺麗に表現するためにはどうしたらよいか?」というところから開発されたものだと思います(あくまで予想)

その点からも回りに見えるものを実際の映像とリンクさせてやる作業には注意がいります。
そんなことしないから必要ないんだけど?っというのが普通の意見だと思いますが、キューブマップを利用したシャドウマップを実装したいときにこの辺が重要になるので注意しておいてください。


なんかまた無駄に長い。
ねよっと。

シャドウマップについて

さてさて影について触れてみましょう

影を作るにはいろいろな方法がありますが一般的に使われているのは「まる影」「ステンシル」「シャドウマッピング」だと思います。
呼び名はいろいろあるそうですが・・



ここでいろいろな種類の影の説明がのっていますので興味のある方は見てみるといいです。

いろいろ試していると次のことがわかってきます。
・足元に落ちるまる影だけってのは流石に寂しい気がする。
・ステンシルは輪郭を作る影だけになってしまう。
・パースを効かせていないシャドウMAPは狭い空間意外では実用性が低い。
・パースペクティブシャドウマップ(PSMやLiSPSM)やカスケードシャドウマップは点光源が使えない。
・全方位キューブシャドウマップは点光源を扱えるがキューブマップの特性上カメラの射影行列が固定されるため光源位置を管理しないといけない。


シャドウマップの仕組みについて軽く触れます。

ほんと軽くなのでこの説明でわかる人は多分100人に一人です。

今書こうとしている物体の1点が「最後に書かれている点だ」とわかるならシャドウマップなんていらないんです。
↑の意味が分かればとりあえず一回全部書き込んでみたデータを元に二回目で影を付ける必要性が見えてきます。
そのデータを絵に持たせてやろう!ってのがシャドウマップです。
cube7.jpg
実際には「光からみた画像を描写する。その際に色の変わりに深度(点までの距離情報)を色情報として画像に預ける作業」を行います。

そして二度目の書き出す際にその情報を使うのですが、

・↑で説明したデータを画像からもってくる
・しかしながら光源からみた画像にはAとBが重なるためにBの情報が深度となっていることに注意
・この深度情報をCとする
・光源からAを見る
・深度を計算してDとする(注)
・CとDを比較してもし一緒なら影にはならない、違えば影になる


↑のことを行うとシャドウマップが実装できるわけだ。
深度の書き込みは以前ブログで紹介した深度を0~1に納める状態をそのまま書き込めばOK
よって
return (UV.z / UV.w);といった感じ。

深度の比較には逆に(注)の場所で得られたW値をUV.z(これは画像から得られた値)にかけることでUV.zに戻してやる。また比較の際は少し誤差がでるので下駄を履かせて調整すると上手くいくと思う。(←これを知らなくて苦労しました)

変換時混乱したら昔の記事も参考にしてください。


ねよっと。



昨日の記事の訂正

昨日の記事の訂正です。

XNAは右手系座標で計算されます よって、

c = cosθ
s = sinθ

とすると
回転行列は次のようになります。

X軸回転

右手系の場合
{ +1 +0 +0 +0 }
{ +0 +c -s +0 }
{ +0 +s +c +0 }
{ +0 +0 +0 +1 }


左手系の場合
{ +1 +0 +0 +0 }
{ +0 +c +s +0 }
{ +0 -s +c +0 }
{ +0 +0 +0 +1 }

Y軸回転

右手系の場合
{ +c +0 +s +0 }
{ +0 +1 +0 +0 }
{ -s +0 +c +0 }
{ +0 +0 +0 +1 }


左手系の場合
{ +c +0 -s +0 }
{ +0 +1 +0 +0 }
{ +s +0 +c +0 }
{ +0 +0 +0 +1 }

Z軸回転

右手系の場合
{ +c -s +0 +0 }
{ +s +c +0 +0 }
{ +0 +0 +1 +0 }
{ +0 +0 +0 +1 }


左手系の場合
{ +c +s +0 +0 }
{ -s +c +0 +0 }
{ +0 +0 +1 +0 }
{ +0 +0 +0 +1 }


計算方法も昨日は左からかけていくと説明しましたが、右手系は右から計算するようです。

いろいろすんません。ほんと。
ねよっと。
プロフィール

あしゅ

Author:あしゅ
ぷぃぷぃ日常。
いつのまにか雑記ブログに。

カテゴリ
最新記事
検索フォーム
最新コメント
リンク
このブログをリンクに追加する
ブロとも申請フォーム

この人とブロともになる

カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。