2014年01月29日

ボリュームレンダリングについて その4

参考文献[1]の実装を試そうとしているのですが、Figure 39-10がよくわからないですね。
Pass1で光源からのレンダリングと視点からのレンダリングの両方を1ポリゴンずつ交互にやっているように見えるんですが、そうなのでしょうか。

とりあえず複数のFBOに1パスで違う絵を描いて、2パス目で合成するプログラムを試しに作成してみることにしました。
マルチプルレンダーターゲットになると思いますので、[2]を参考に改変させていただきました。

内容

半透明合成+法線ベクトル及び深度値のエッジ

Pass 1
1. GL_CULL_FRONTで前面消去して奥のレンダリング
1.1. texture0:RGB(diffuse+ambient)
1.2. texture1:法線ベクトル+深度値
2. GL_CULL_BACKで背面消去して手前のレンダリング
2.1. texture0:1.1.をそのまま出力
2.2. texture1:1.2.をそのまま出力
2.3. texture2:RGB(diffuse+specular+ambient)
2.4. texture3:法線ベクトル+深度値

Pass 2
3. エッジの計算
3.1. texture1の法線ベクトル及び深度値にラプラシアンフィルタを適用
3.2. texture3の法線ベクトル及び深度値にラプラシアンフィルタを適用
3.3. // エッジ部分が暗くなるように調整
   float edge = 1.0-(フィルタの値4つの和);
4. 厚みの計算
4.1. // 厚いところほど暗くなるように調整
   float depth = 1.0-3.0*(texture1.a-texture3.a)^2
5. 色の計算
5.1. // 奥側にだけ厚みの影響を掛ける
   vec4 color = mix(texture2, depth*texture0, 0.5);
6. 出力色の計算
6.1. glFragColor = vec4(edge*color.rgb, 1.0);

実行結果

奥側の色と深度
mrt_001.png mrt_002.png

手前側の色と深度
mrt_003.png mrt_004.png

色の合成結果とエッジ(赤:法線ベクトル、緑:深度値)
mrt_005.png mrt_006.png

最終結果

ちょっとエッジが太すぎですかね。後半の立方体は錯視っぽいのが起こってちょっと気持ち悪いです。
次回はちゃんとボリュームレンダリングのお話に戻りたいと思います。

[1] http://http.developer.nvidia.com/GPUGems/gpugems_ch39.html
[2] http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20101208

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

2014年01月25日

ボリュームレンダリングのテスト その3

前回の続きです。

ボリュームレンダリングの手法として、奥側から加算していくBack-to-Frontと、手前側から加算していくFront-to-Backがあります。
OpenGLで行う場合、Blend関数を使って実現するのですが、それぞれの方法をどう設定すればよいのかについてメモしておきます。

src: 今回出力する色[0, 2^m-1]
dst: 現在バッファに出力されている色[0, 2^m-1]
color: 上記の2つを合成した結果の色[0, 2^m-1]

src_factor:srcに掛ける倍率[0,1]
dst_factor:dstに掛ける倍率[0,1]

color = src*src_factor + dst*dst_factor

[1]にはkRとかよくわからない値が出てきていて何のことかと思いましたが、
mがbit数なので、たとえばunsigned char(m=8ビット)で色を表現している場合、
2^8-1=255ということで、kR=255で割って、[0,1]の範囲にsrc_factorやdst_factorを調節しているだけのようです。

GL_SRC_COLORだと、factorが[0,1]の色になるので、色の乗算に使えるようです。
GL_SRC_ALPHAだと、factorはαの値[0,1]になるので、αブレンドの場合はこちらを使います。
この辺りの組み合わせをWeb上で試せる良さそうなサイトがありました[2]。

事前準備

Blend関数を使う場合は、有効化と、デプステストの無効化(特にFront-to-Backの場合)が必要です。
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glBlendFunc(..., ...);
...
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);

Back-to-Front合成の場合

ポリゴンは奥からレンダリングしていきます。普通のαブレンドでもできるみたいですが、参考サイト[3]に合わせます。

Ci^=Ci+(1.0-Ai)Ci+1^, // 出力のRGBは、現在のRGB+(1.0-現在のα値)×一個前のRGB
Ai^=Ai+(1.0-Ai)Ai+1^, // 出力のαは、現在のα+(1.0-現在のα値)×一個前のα

これをOpenGLのブレンド関数で表すと、
srcは1倍、dstは1.0-Ai(ソースのα)を掛けた量になるので、
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);を使います。

Front-to-Back合成の場合

ポリゴンは手前からレンダリングしていきます。
Ci^ = (1.0-Ai-1^)Ci+Ci-1^, // 出力のRGBは、(1.0-一個前のα)×現在のRGB+一個前のRGB
Ai^ = (1.0-Ai-1^)Ai+Ai-1^, // 出力のαは、(1.0-一個前のα)×現在のα+一個前のα

これをOpenGLのブレンド関数で表すと、
srcは1.0-Ai-1^(デストのα)、dstは1倍を掛けた量になるので、
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);となります。

Fragment Shaderの出力

Fragment Shader側では、GL_ONEになっているので、colorの部分はシェーダ側で掛けておく必要があります。多分。
// 属性値を関数を通して計算した透明度
float alpha;
// 属性値を関数を通して計算した色(αは掛かっていない)
vec3 rgb;
// 3dテクスチャから属性値をとってきて透明度に変換したり色を直接取得したり
...
// GL_○○○_ALPHAではなくGL_ONEにしているので自分でalphaを掛ける
gl_FragData[0] = vec4(alpha*rgb, alpha);

ポリゴンをzの手前から描画してFront-to-Backで合成した結果と、奥側から描画してBack-to-Frontで合成した画像が一致したので、多分これで合っていると思います。

[1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd318368(v=vs.85).aspx
[2] http://www.andersriggelsen.dk/glblendfunc.php
[3] http://http.developer.nvidia.com/GPUGems/gpugems_ch39.html

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

2014年01月14日

ボリュームレンダリングのテスト その2

前回の続きです。

GPU Gems 1 Chapter 39[1]を参考に、スライス(テクスチャ)ベースのボリュームレンダリングを試していきたいと思います。
で、GLSLで3Dテクスチャを使用することになったのですが、ひさしぶりで使い方がわからなくて困りました。

以下参考文献[2][3]からのメモです。適当なので参照元を確認してください。

レンダリングするとき、どのテクスチャユニットを使用するのか指定する必要があります。
それにはglActiveTexture(GL_TEXTURE0+i)で、i番目のテクスチャユニットを指定します。

使用できるテクスチャユニットの数は、GL_MAX_COMBINED_TEXTURE_IMAGES_UNITSに定義されているようです。
で、GLSLでSamplerを使ってテクスチャを参照するためには、Samplerとこのテクスチャユニット番号を関連付ける必要があります。
これにglUniform1i等のGLSLへの設定関数を使用します。

例えば、GLSLの方でSampler3D texmap3d;と宣言したサンプラに、3Dテクスチャを割り当てて、4番目のテクスチャユニットを使用したい場合は、以下のようになります。

1. 3Dテクスチャの準備例(OpenGLの準備時)
    // ↓これはテクスチャのID
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_3D, texId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RED, 32, 32, 32, 0, GL_RED, GL_FLOAT, data);

2. テクスチャユニットの設定(GLSLの初期設定時)
    // ↓これはサンプラの場所のID
GLint tex3dLoc = glGetUniformLocation(program, "texmap3d");
glUseProgram(program);
// ↓このサンプラを使う場合、テクスチャユニット4番目を使用という関連付け
// (間違えてtexIdを指定してしまうことがある)
glUniform1i(tex3dLoc, 4);

3. テクスチャのデータを関連付け(レンダリング時)
    // ↓4番目のテクスチャユニットをターゲットに設定
glActiveTexture(GL_TEXTURE0 + 4);
glEnable(GL_TEXTURE_3D);
// ↓これは3Dテクスチャの関連付けなのでtexIdを使用
glBindTexture(GL_TEXTURE_3D, texId);

以前同じようなことを書いたような気がしますが、多分気のせいですね。

続いて、参考文献[1]にあるスライスの計算方法です。

視線から見た領域のスライスは、ボリューム領域のAABBにおいて、視線のレイ上の点とその方向ベクトルを法線ベクトルとする平面が作る断面となります。
volumerendering_001.png
直方体を平面できった場合、最大6個の頂点による多角形ができるようですので、その座標を計算する必要があります。
volumerendering_002.png
直方体と平面の接触座標の計算方法はよくわからなかったので、直方体を作る12本の辺と平面の接触判定を行い、その接触点を頂点とする断面をポリゴン化してレンダリングすることにしました。
線分と平面の衝突判定については[4][5]を参考にさせていただきました。

最大6個の頂点が見つかれば、それを三角ポリゴンにします。今回、6個の頂点の重心を中心に、各頂点へのベクトルを計算して、その内積などを使って左回りにソートしました。
この辺は[1]等を詳しくみればもっと最適化できるとは思います。

実行結果

今回は3Dテクスチャ固定+ポリゴン数百枚程度ですので、リアルタイムで動作しています。
まずはスライスができているかどうかの確認です。


ボリュームのAABBを10分割した場合


ボリュームのAABBを200分割した場合

10分割だと荒いですが、視点に面したスライスができていることがわかります。
200分割だとなんとなくボリュームレンダリングという感じがします。

続いて、前回と同様にボリュームデータを読み込んでレンダリングした結果です。


オレンジ(200分割)


ロブスター(200分割)

属性値を同じα値で重ね合わせただけですので、属性値が大きい部分だけ白くなっている結果です。
データ間隔を均等にしてしまっているのでちょっとスケーリングがおかしいですが、それっぽくはなっています。

次回は、[1]では視線と光源のハーフベクトル方向にスライスして、2パスレンダリングすると陰影が綺麗に出るようなことが書かれていますので、それを試せればと思います。

[1] http://http.developer.nvidia.com/GPUGems/gpugems_ch39.html
[2] http://www.opengl.org/wiki/GLSL_:_common_mistakes
[3] http://www.opengl.org/wiki/GLSL_Sampler#Binding_textures_to_samplers
[4] http://marupeke296.com/COL_3D_No3_LineToPlane.html
[5] http://marupeke296.com/COL_3D_No4_LineToPlanePolygon.html
web拍手 by FC2
posted by シンドラー at 23:25 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年01月11日

ボリュームレンダリングのテスト

スプライン曲線ができたので、複数ウィンドウを表示[1]してボリュームレンダリングを試してみました。

実行結果

オレンジのデータを表示してみました。データは[2]のものを使用させていただいております。



右の3つのウインドウは、各属性値に対するR、G、Aの大きさを設定しています。
なぜかウィンドウが4つまでしかできなかったのでBはありません。

左のウィンドウは、レイキャスティングでBack-to-frontで計算していたと思います。
CPUで計算していて遅いのでキーを押したときだけ再描画するようにしています。

まだ法線や陰影などをちゃんと計算できていないので、試していきたいです。

[1] http://d.hatena.ne.jp/etopirika5/20080910/
[2] http://www9.informatik.uni-erlangen.de/External/vollib/

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

2014年01月08日

3次スプライン曲線のテスト

曲線の生成に3次スプライン曲線を試してみました。
1個の多項式で曲線を近似するのは大変なので、3次の多項式を滑らかにつないで曲線を作るということのようです[1]。
連立一次方程式はEigenで計算しました。

実行結果

赤い点が制御点、緑の線は適当なsin関数で、青い線がスプライン曲線です。
左ドラッグで制御点の移動、中クリックで制御点の削除、右クリックで制御点の追加ができるようにしたので、ある程度自由に曲線を作れると思います。



[1] http://www5d.biglobe.ne.jp/~stssk/maze/spline.html
web拍手 by FC2
posted by シンドラー at 22:18 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年01月02日

ヤコビ行列を使ったIKについて その4

新年あけましておめでとうございます。今年もよろしくお願いします。

というわけで、前回の続きです。3次元に拡張してみたところ、あっけなくうまくいきました。
また、正月休みということで、昔作ってたPMD表示のプログラムをゆっくり今のプログラムに移植していました。

実行結果

かかとの左足IKをマウスでぐるぐる動かしています。IKは最大20回更新しています。



ヤコビ行列のIKで試しています。昔のプログラムだとオイラー角はZXY回転で計算していたので、そうするように書き換えています。PMDのIKは親を持たないワールド座標で位置を指定して、ターゲットとしているボーンがIKの座標を目的として動いていく仕様のようですね。PMXはどうかわかりませんが。

ボーンアニメーションはVBOでCPU側でやってたりするので、ここはOpenCL等GPU側でできるようにしたいですね。
web拍手 by FC2
posted by シンドラー at 23:50 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。