2015年09月08日

Unity 5でG-Bufferのテスト その3

続きです。[1]には、16.4にテクスチャベースのSSSについても書かれていて、割とよく使われている方法かと思います。なぜかこれまでやってなかったのでUnity 5とG-Bufferで適当に試してみることにしました。

まずは、テクスチャ座標を展開したような画像を生成します。Vertex Shaderで、テクスチャ座標(0〜1)が取得できると思いますが、これを-1〜1に広げてフラグメントシェーダの頂点として渡せばよい、ということになります。この辺、MMDモデルなどでは複数マテリアルでテクスチャ座標が重なっていたり、顔のテクスチャが半分しかなく、顔の左右で同じテクスチャ座標を持っていたりして、一工夫が必要です。

そこでまずは簡単のために、CGの論文?などでよく見るHeadモデル[3]で試してみることにしました。Unityに.objを読み込むとオブジェクトが複数Groupに分かれていたり表示がおかしかったので、一旦Metasequoiaで読み込んで、1オブジェクト1マテリアルとして出力しなおしました。あとはバンプマップの.pngもうまく読み込めなかったので.bmpにしました。[2]によると16-bit .pngとあるので、それで駄目だったのでしょうか。

MaterialをStandard Shaderにしてレンダリングした結果は下記のようになります。テクスチャが細かいのとGIなのでこれでも十分な気がしますね。

Unity5_sss_001.png

以下手順です。

0. カメラにSSSTest.csを設定、SSS MatにCustom/SSSShaderを割り当てたマテリアルを設定
SSSTest.cs

1. テクスチャ座標を展開した画像をRenderTextureに出力

多分Unityのビルドイン変数や関数を使えばいいのだと思いますが、C#スクリプトからワールド座標や光源の位置や行列を渡しています。[1]では単純に-1〜1にしていますが、上下逆になるのでy座標は反転させています。

UnwrapShader.shader

Unity5_sss_002.png

また、本来はdotNLだけですが、周辺を明るくしたかったのでリムライティングも入れてみることにしました。

Unity5_sss_003.png

2. テクスチャ座標やオブジェクト番号などをG-BufferとしてRenderTextureに出力

最終的にDeferred Shadingで合成するためには、普通にレンダリングした結果と、その座標が持つテクスチャ座標も必要になります。Unityが提供しているG-Bufferには、テクスチャ座標がなさそうな感じでしたので、自前で出力します。ついでにオブジェクト番号や、MMDモデルなどの場合はマテリアル番号なども出力する必要があるかと思います。

RenderGBuffer.shader

Unity5_sss_004.png

Skydomeはテクスチャ座標がないので黒、床はオブジェクト番号を設定していないのでテクスチャ座標のみ、モデルはBlueに1.0を設定しているので、これを使ってSSSする部分とそれ以外を区別します。

3. 1. のテクスチャを適当にブラーして、通常のレンダリング結果と合成

ブラーについては、[1]のものを参考に、[3]のブラーの重み付けを参考にやってみました。本当は複数枚のブラー画像を作成して合成する必要がありますが、手抜きで1枚しか使っていません。あと、歪みの補正みたいな話も[3]には載ってますがそれもやってません。

SSSShader.shader

ブラー画像はこんな感じです。

Unity5_sss_005.png

そういえばRenderTextureの背景色を肌色っぽい色にしていたはずなのに、なんで黒色なんでしょうか。その辺含めてバグがありそうですね。

実行結果

SSSなし
Unity5_sss_009.png
リムライティングなし
Unity5_sss_007.png
リムライティングあり
Unity5_sss_008.png
ブラーしたテクスチャ色のみ
Unity5_sss_006.png

[1] http://http.developer.nvidia.com/GPUGems/gpugems_ch16.html
[2] http://graphics.cs.williams.edu/data/meshes.xml
[3] http://news.mynavi.jp/column/graphics/061/

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

2015年09月02日

Unity 5でG-Bufferのテスト その2

G-Bufferの使い道ということで、とりあえず[1]の16.3 Simulating Absorption Using Depth Mapsを試してみようとしたのですが、色々と詰まりましたのでメモしておきます。まだ色々間違いがありそうですが。

1. RenderWithShaderで光源からの深度値のレンダリングはForwardで行う

レンダリングパスを全部Deferredにしていると、OrthogonalではレンダリングできるのにPerspectiveでは動かないという状態になりました。[2]に同じ症状の人がいましたので、Forwardにして対応しました。

2. 深度値のマクロが色々あってどれを使えばいいのかわからない

これもちゃんと一つ一つシェーダのコードを見ていけばわかるんだとは思うのですが、UnityCG.cgincに色々便利関数が定義されているんですがどれを使ってシェーダ上でどれを使えば参照できるのかなどがわかりにくかったです。まだ怪しいので要検証ですが。

3. 右手系、左手系、OpenGL、DirectXなのかわからない

色々な環境に対応するために色々な工夫がされているようですが、座標系がわかりにくかったです。環境設定のせいかもしれませんが、C#スクリプトでMatrix4x4を使う分にはOpenGL系に見えるんですが、深度値の範囲などシェーダ側ではDirectX系になっているようで、G-Bufferからワールド座標の計算などがなかなかうまくいかなくて大変でした。[3][4]にあるように、DirectXを使用する場合には、C#スクリプト側でDirectX用にプロジェクション行列を変換しないといけなかったようです。

手順
1. 平行光源とそれを子に持つサブカメラを追加する
2. 上記サブカメラにはRenderTextureにRenderWithShaderを使って深度値を書き込むスクリプトとシェーダを設定する。この辺りは[5][6]も参考にさせていただきました。
DepthRenderer_002.cs
RenderDepth_002.shader

3.メインカメラにG-Bufferを取り扱うスクリプトとシェーダを設定する。サブカメラの位置は、対象となるThirdPersonCharacterのZ軸方向後ろ上に角度で指定した位置に設定する(Update関数内)。
GBufferTest_002.cs

シェーダでは、G-Bufferでメインカメラで映っているピクセルのワールド座標を計算し、その座標をサブカメラから見た場合のワールド座標との位置差(length)を使って薄いところは明るく、厚いところは暗くするようにする。
TestShader_002.shader

C#スクリプトからマテリアルには、SetTextureやSetMatrixを使うと色々データを渡せて、それをシェーダ側で参照できることがわかりました。

実行結果

スクリプトでキャラクタが移動したり光源の角度を変えてもうまく光源からの深度値が取れるようにサブカメラの位置を制御しています。

gbuffer_test002_004.png

サブカメラの角度を変えると、一応光源の位置に応じた厚みで色が変わります。

0度
gbuffer_test002_002.png
30度
gbuffer_test002_001.png
60度
gbuffer_test002_003.png

[1] GPU Gems Chapter 16. Real-Time Approximations to Subsurface Scattering
[2] Unity Answers - A Problem About RenderWithShader and Camera Orthographic
[3] Screen Space Reflections
[4] Unity Answers - How do I reproduce the MVP matrix?
[5] kode80 - Screen Space Reflections in Unity 5
[6] Unityのオフスクリーンレンダリング
web拍手 by FC2
posted by シンドラー at 01:31 | Comment(0) | TrackBack(0) | Unity 5 | このブログの読者になる | 更新情報をチェックする