2013年05月24日

球面調和関数について その8

頂点毎に遮蔽情報のSH係数を事前計算しておいて、実行時に予め計算した係数を読み込むのはいいのですが、読み込んだ係数をGLSLに渡す必要があります。
テクスチャで渡したり色々方法があるとは思いますが、今回はVertex Attributeとして渡すことにしました。
これはボーンのIndexと影響度などを頂点毎に設定して、GLSLに渡すときなどによく使われているようです。

見たところ、Vertex Attributeとして渡せるのは16個までの変数のようでしたので、3次の球面調和関数として遮蔽情報及びパノラマ画像のSH係数を渡すことにしました。
4個までのデータであれば、特に気にすることなくvec4で受け取れば良いのですが、16個の場合でmat4として受け取る場合は一工夫必要なようです[1]。
(最新のバージョンでは違うのかもしれませんが)

//---------------------------------------------------------
// 4個のfloat型データ(vec4)として渡す
glEnableVertexAttribArray(attribloc);

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(attribloc, 4, GL_FLOAT, 0, 0, NULL);
//---------------------------------------------------------

//---------------------------------------------------------
// 16個のfloat型データ(mat4)として渡す
for (int i=0; i<4; i++)
{
glEnableVertexAttribArray(attribloc + i);
}

for (int i=0; i<4; i++)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo[i]);
// 同じバッファ領域でOFFSETで制御する場合はNULLでは駄目
glVertexAttribPointer(attribloc + i, 4, GL_FLOAT, 0, 0, NULL);
}
//---------------------------------------------------------

後はGLSLのバーテックスシェーダで受け取り、頂点補間するためvaryingで渡します。

hoge.vert
attribute in mat4 shMat;
varying out mat4 fragShMat;

void main(void)
{
fragShMat = shMat;
...
}

hoge.frag
varying in mat4 fragShMat;
uniform mat4 panoR, panoG, panoB; // パノラマ画像から計算したSH係数

void main(void)
{
...
vec3 dotCol = vec3(0, 0, 0);
for (int i=0; i<4; i++)
{
dotCol.r += dot(fragShMat[i], panoR[i]);
dotCol.g += dot(fragShMat[i], panoG[i]);
dotCol.b += dot(fragShMat[i], panoB[i]);
}
gl_FragData[0] = vec4(dotCol, alpha);
...
}

実行結果

本当はスカイドームで周りの環境も描画するのですが、とりあえずモデルのレンダリングだけしてみました。



肩の辺りとか顔とか一部おかしくなっていますが、一応想定した結果が得られているのではないでしょうか。
事前計算には時間がかかりますが、一度計算しておけばリアルタイムでGIが計算できています。

現状モデル自身の自己遮蔽情報しか計算していませんので、床など他のオブジェクトを追加しても結果に反映されません。
それぞれのオブジェクトの遮蔽情報を計算しておいて、実行時にうまくやれば変形しない物体の移動や回転もできるそうですが、そのやり方についてはまだ調べていません。

[1] http://www.opengl.org/discussion_boards/showthread.php/164099-how-to-specify-a-matrix-vertex-attribute
web拍手 by FC2
posted by シンドラー at 21:57 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2013年05月15日

球面調和関数について その7

遮蔽情報を各ピクセル毎に計算し、球面調和関数圧縮したものと、スカイドームを圧縮したものの内積を計算して色を計算してみました。

実行結果



視点が回っているように見えて、実はスカイドームのスケーリング係数に回転行列を掛けています。
合っているのかどうかわかりませんが、やっとここまでできました。
まぁ理論的なことが理解できていないので結局実装してみただけになってしまいましたが。
5000個の光源を計算するよりはかなり高速です。

次はピクセルごとではなく頂点毎に計算して色々な視点からでも対応できるようにするのと、その時の補間はスケーリング係数の線形補間でいいのかどうかの確認、GLSLを使ってリアルタイム計算、辺りでしょうか。
web拍手 by FC2
posted by シンドラー at 19:34 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2013年05月14日

球面調和関数について その6

球面調和関数使ったImage Based Lightingは基本静的オブジェクト用ですが、光源は自由?に動かせるようなことが書かれていました。

例えばスカイドームを回転させたい時などは元に戻してθやΦの値を変更してまた圧縮、という面倒なことをしないといけないのかと思ったのですが、どうやらスケーリング係数自体に回転行列を掛けたのでいいそうです[1]。

この回転行列の計算方法も難しそうでしたので、参考サイトのソースコードを参考にさせていただきました。
6次でもそのまま使えましたが、回転行列の計算にも時間がかかる気がします。
うまい高速化の方法があるんでしょうかね。

実行結果

オイラー角で指定できるようになっていましたので、とりあえずY軸?回りに一周させてみました。



後は、スカイドームの画像を球面調和関数で圧縮した係数を、法線ベクトルが基準となるような回転行列を掛けて、遮蔽情報を球面調和関数圧縮した係数との内積をとれば、やっと目的の計算ができることになる、はずです。

係数に行列掛けたので結果が得られたり係数の内積で色が計算できたり思いついた人はすごいですねぇ。

[1] http://lucille.sourceforge.net/blog/archives/cat_precomputed_radiance_transfer.html

web拍手 by FC2
posted by シンドラー at 01:38 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2013年05月12日

球面調和関数について その5

パノラマ画像から球面調和関数のスケーリング係数を計算する方法についてのメモです。

以前は-π〜πの間で計算していましたが、今回は0〜2πで計算しています。
おそらくどちらでも計算結果は変わらないと思います。

sh005_001.png

スケーリング係数の計算は球面上の領域の積分になります。

sh005_002.png

以前の記事で上下が明るくなりすぎておかしい、と書きましたが、やはり積分の計算のところを間違えており、sinθを掛けていなかったのが原因だったようです。

実行結果

sh005_004.png

一応キューブマップの場合と同じような結果が得られていると思いますので、合っているのではないかと思います。
web拍手 by FC2
posted by シンドラー at 21:36 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2013年05月08日

球面調和関数について その4

気がついたらGW終わってましたね・・・。
動作確認&息抜きに2次元に落とし込んで球面調和関数でお絵かき?するプログラムを作ってみました。
2次元といってもθ=π/2のときのみを計算しているだけで、基本は3次元のままです。

実行結果



マウスで点を打った座標をワールド座標に変換して球面調和関数のスケーリング係数を計算しています。
緑の点は、圧縮元のデータを正の値、赤の点は負の値として係数計算をしているので、出したいところに緑の点を打って、へこませたいところに赤い点を打てば、ある程度望んだ輪郭線を描くことができます。

l=6までのデータしか使用していませんので、ものすごく起伏の激しい図形は無理みたいです。
lをもっと増やした場合、形が変になってうまく近似できなくなってしまいましたので、lを増やした場合の計算を間違えているのかもしれません。

とりあえず動作確認としては-π〜πを0〜2πの範囲に変換する計算を間違えていたのが見つかったので良かったのでしょうか。

web拍手 by FC2
posted by シンドラー at 18:52 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2013年05月02日

球面調和関数について その3

いつの間にか5月ですね。

前回の続きで、圧縮の方をやってみました。
パノラマ画像をつかってやってみたかったのですが、どうも積分のところの計算が間違っているのか上下の辺りが明るくなりすぎてうまくいかなかったため、[1]を参考にさせていただき、キューブマップ画像で行うことにしました。

1.パノラマ画像→キューブマップ画像
SH3_002.png
OpenGLの座標系を考えつつ、キューブマップ画像に変換します。
パノラマ画像はいつものように[2]の画像を使用させていただきました。

2.キューブマップ画像の圧縮/展開
6次までの係数(49個)に圧縮して展開した結果が次のようになりました。
なんとなくそれっぽいのであっているのかなと思います。
SH3_003.png

3.遮蔽情報の計算
これをどうやるのがいいのかいまいちよくわかりません。
いくつかのサイトでモデルの頂点毎に計算とありましたが、例えば1個のポリゴンの3頂点で遮蔽情報を球面調和関数で圧縮したとして、ポリゴン上の任意の点の係数はどうなるんでしょうか。法線ベクトルなら3頂点から補間しますが、SHの係数を補間してしまっても問題ないんでしょうかね。
元の論文などには書かれているのかもしれませんが読んでいないので困ったものです。

後は遮蔽情報は、キューブマップの各画素方向にレイを飛ばして衝突判定して衝突したら遮蔽されている、ということで計算していますが、ものすごく計算時間がかかります。自前の衝突判定が遅いだけかもしれませんが、効率良く遮蔽情報を計算する方法ってないんでしょうかね。

というわけでまだ怪しいですが、とりあえず床と球の簡単なシーンにおけるある頂点での遮蔽情報を同様に6次で圧縮/展開した結果が以下のようになりました。

シーン(環境マッピングで使ったものを流用)
skydome000_2500.bmp
遮蔽情報(圧縮前)
SH3_000.png
遮蔽情報(圧縮後展開)
SH3_001.png

キューブマップの場合と違って元の情報と結構違って見えますがあってるんですかね。

一応今回はここまでで、遮蔽情報の計算が終わればやっとSHを使ったレンダリングができそうです。

[1] http://asura.iaigiri.com/OpenGL/gl69.html
[2] http://www.nicovideo.jp/watch/sm10968092
web拍手 by FC2
posted by シンドラー at 19:32 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする