Monday, November 27, 2017

[UE4] マンデルブロ集合マテリアルを作る

GLSL Sandboxにマンデルブロ集合があり、面白そうだったのでちょっと使いやすそうに加工してからUE4のマテリアルにしてみました。



ループがあるのでCustomノードに書きます。発散の判定は絶対値が2を超えた時点とし、発散と判定されるまでの計算回数が多い程輝度が上がるようにしました。



探検できるように300倍に拡大した'Plane'にマテリアルを貼り付けます。

Spectator Pawnで探検します。

離れてみると分かりにくいですが、近くから輪郭部を見ると非常に輝度が高いのがわかります。集合の左側は特に輝度が高く、更に拡大しても複雑なパターンが現れてきそうです。

プロジェクトをgithubにアップしました。

Thursday, November 16, 2017

[ゲルストナー波] 水面に物を浮かべる

ゲルストナー波の実装は前々回前回をご覧ください。


Copyright (c) 2011 NVIDIA Corporation. All rights reserved.

NVIDIAのIsland11を改造して作っています。

題の通り物を水面に浮かべたいのですが、ゲルストナー波はHLSLで実装したので現状CPUから水面の動きを取得する方法がありません。しかし、波のパラメータはCPU側でも分かっているので、C++でも同じ計算を実装すれば目的を達成できます。


ほぼ機械的にHLSLからそのまま持ってきただけです。ImmutableCBはコンスタントバッファになる構造体で、GPUと共有する波のパラメータの集まりです。この波のパラメータは一度決定したら書き換えません。変化するのはtimeだけで、現在時刻だけ毎フレームC++とHLSLで同期すれば任意の地点の海水面の移動量が一意に決定します。

あとは、描画したいメッシュの座標をCalcGerstnerWaveOffsetにlocationとして渡せばVec3の移動量が取得できるので、それを足して描画すれば上のアニメーションが得られます。

更に、ここからキューブを波に沿って傾けることを考えてみます。


Copyright (c) 2011 NVIDIA Corporation. All rights reserved.

HLSLでやったように微分して法線を得る事でもできそうですが、予想される結果として戦艦のような大きなものを局所的な法線で傾けるとおかしくなりそうです。今回は海面から任意の3点を取得してそれの属する平面の法線を使うことにします。これなら、大きな物体は3点をなるべく離れた場所に設定することで傾きすぎないようにするなどの調整ができます。

キューブの中心からやや離れた3点を波で動かし、offsetXZ, offsetNegX, offsetNegZの3点を得ました。ここから得た法線と真上に向かうベクトルとのなす角を回転量とし、同様に法線と真上に向かうベクトルとの外積で回転軸を求めています。

Sunday, November 5, 2017

[Vulkan] vkCmdPipelineBarrierで指定するVkPipelineStageFlagBitsとは? [SDC 2017]

Samsung Developer Conference 2017で行われた、VulkanのvkCmdPipelineBarrierの使い方の技術セッションがYouTubeで公開されています。

vkCmdPipelineBarrierに指定するsrcStageMaskとdstStageMaskの指定方法に関して説明されており、Khronosの文書が関数リファレンスのレベルしか言及していない現状で、動作原理を解説する貴重なセッションとなっています。

連続する2つのパスはGPUで並行に実行される可能性がありますが、前のパスの結果に後のパスが依存する場合、vkCmdPipelineBarrierを置くことで後のパスを待たせる必要があります。ただし、GPUコア間の並行性が失われて待機時間が生じます。これがPipeline Bubbleと呼ばれます。

srcStageMaskは前のパスのパイプラインステージ(例:vertex shader等)、dstStageMaskは後のパスのパイプラインステージを表します。前のパスのsrcStageMaskで指定のステージが終わるまで、後のパスのdstStageMaskで指定のステージは始めてはいけないという意味になります。

VK_PIPELINE_STAGE_TOP_OF_PIPE_BITとVK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BITは存在しない一種の仮想のパイプラインステージで、それぞれ全てのパイプラインステージの前と後を意味しています。

保守的には、srcStageMask=VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT、dstStageMask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BITとすることで並列動作を出来なくします。

逆に、srcStageMask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT、dstStageMask=VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BITとすることで、2つのパスは並列に動き出すそうです。

残念ながら、どのように依存関係を解決しながら平行動作させるかには言及されていませんでしたが、2つのパスの依存関係の無い場合には並列動作させることでpipeline bubbleを取り除こうという趣旨と思われます。

また、AMDのサイトでもVulkan barriers explained と題してvkCmdPipelineBarrierを解説しています。

おそらく最も使用頻度が多いのが、前のパスで作った絵を次のパスのfragment shaderで使うというシナリオだと思いますが、srcStageMask=VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT、dstStageMask=VK_PIPELINE_STAGE_FRAGMENT_SHADER_BITとすればよいそうです。これなら前のパスが絵を描き終わる前に次のパスの頂点処理を平行させることが出来そうです。

DX12では、ID3D12GraphicsCommandList::ResourceBarrierでリソースのステートを遷移させるには前後のD3D12_RESOURCE_STATESを指定すればタイミングはドライバが決めてくれました。Vulkanでは前後のVkImageLayoutに加えてVkPipelineStageFlagBitsでタイミングまでプログラマが把握して指定しなければいけません。

Sunday, October 22, 2017

[Vulkan] GeForceでスワップチェーンはRGBAでは無くBGRA

グラフィックスAPIにおいて紛らわしかったカラーチャンネルの並びの問題ですが、DirectX11やDirectX12ではRGBAだけ使えば良く、BGRAは忘れても良い存在になっていました。ところが、GeForceやIntelの統合型GPUのVulkanではスワップチェーンがBGRAでなければいけないようです。

vkGetPhysicalDeviceSurfaceFormatsKHR関数で列挙したと思われるリストが、gpuinfo.orgにあります。(Surfaceタブ→Surface formatsタブ)
https://vulkan.gpuinfo.org/displayreport.php?id=1895#surface - GTX 1050
https://vulkan.gpuinfo.org/displayreport.php?id=1922#surface - Mali-G71
https://vulkan.gpuinfo.org/displayreport.php?id=1945#surface - Radeon RX 560
https://vulkan.gpuinfo.org/displayreport.php?id=1973#surface - Adreno 540

AdrenoやMaliは逆にRGBAのみのサポートで、RadeonはRGBAとBGRAの両方をサポートしていることがわかります。

レンダーターゲットのフォーマットはRenderPassに含まれ、RenderPassはPSOが参照します。スワップチェーンに直接描画するPSOはデバイスに合わせてRGBAかBGRAかどちらかで生成されたRenderPassを指定しなければいけないことになります。

中間バッファにBGRAを使うことは無いはずなので、結局Vulkanでは同じシェーダでも「描画対象がスワップチェーンか中間バッファか」によって出来上がるPSOが別物になると考えなければいけません。

PSO生成の際のパラメータが増えて管理が煩雑になりそうですが、実際の所スワップチェーンに直接描画するものといえばUIかポストプロセスくらいなので、場合分けは難しくないと思います。

Sunday, October 15, 2017

[Vulkan] RadeonでVK_FORMAT_D24_UNORM_S8_UINTのdepth stencilテクスチャが作れない

近年多く使われていたD24_UNORM_S8_UINTのdepth stencilテクスチャはPCのVulkanで生成できないケースがあります。例えば、Radeon 560 RXでは、D24_UNORM_S8_UINTが未サポートです。代わりにD32_SFLOAT_S8_UINTを使わなければいけません。
https://vulkan.gpuinfo.org/displayreport.php?id=1945#formats

GTX 1050の場合はD24_UNORM_S8_UINTがサポートされています。
https://vulkan.gpuinfo.org/displayreport.php?id=1895#formats

DirectX12ではRadeon 560 RXでもDXGI_FORMAT_D24_UNORM_S8_UINTが作れましたが、内部でエミュレートしていると思われます。

Vulkanといえば、モバイルデバイスも気になるところですが、Adreno 540、Mali-G71共に、D24_UNORM_S8_UINTしか対応していないようです。Radeonとは逆ですね。
https://vulkan.gpuinfo.org/displayreport.php?id=1925#formats
https://vulkan.gpuinfo.org/displayreport.php?id=1922#formats
いずれのプラットフォームもD32_SFLOATは存在しているので、stencilを使わないならD32_SFLOAT、stencilを使いたいならプラットフォーム毎に分岐しなければいけなさそうです。

Monday, October 9, 2017

ゲルストナー波の法線を計算する

前回に引き続き、ゲルストナー波をやります。

ゲルストナー波の法線を計算してみます。安直な方法としては、CalcGerstnerWaveOffset関数が波のあらゆる地点の頂点位置の移動量を求められるものであることを利用し、x方向とz方向に1センチずらしてそれぞれCalcGerstnerWaveOffsetを呼び、これを三角形に見立てて法線を求めます。

これでも実用上問題無いレベルになるとは思いますが、GPU GemsのEffective Water Simulation from Physical Modelsによると、Xで偏微分するとBinormal、Yで偏微分するとTangent、それらの外積からNormalを求めることができます。親切にもNormal, Binormal, Tangentそれぞれの式がEquation 10~12として提示されていますので、そのままHLSL化してみます。ただし、Island11はYが上、論文ではZが上なので、YとZを入れ替えてあります。そのせいで、Normal = Binormal X Tangentとはならず、Normal = Tangent X Binormalになっています。


あえてくどくどと関数を分けてありますが、論文で使われている式や変数名をなるべく残すためで、現場で使う場合は1ループで一気にfloat3x3の回転行列まで求めると良いと思います。

Island11のドメインシェーダの既存の法線はそのままワールド空間の法線として使われていましたが、それをゲルストナー波の接空間(tangent space)とみなしてワールド空間に配置します。


次回は水面に物を浮かべてみます。

Saturday, October 7, 2017

HLSLでゲルストナー波を実装

NVIDIAのIsland11サンプルを改造してゲルストナー波を加えてみました。

Copyright (c) 2011 NVIDIA Corporation.


ゲルストナー波の実装例はいくつかのサイトで紹介されていますが、多くはGPU GemsのEffective Water Simulation from Physical ModelsのEquation 9の式が引用されています。

以下はこの式をHLSLで実装してみたものです。
引数のvで海面の頂点座標を受け取り、その位置においてどれだけ頂点を移動させればいいのかオフセットを返しています。C++側から乱数で生成した100個の波のパラメ ータを受け取っていますが、試行錯誤の結果20個だけ使っています。

C++でパラメータを生成するコードは以下です。

randomRadの計算で、最後の0.3fを無くすと全方向への波をランダムに生成することになります。0.3を掛けてある程度似た方向に向けて生成するようにしています。

amplitudeは波の高さで、waveLengthは波の幅です。特に決まった式があるわけではなく、見た目から数値や式を適当に選んだ結果です。ただし、GPU GemsによるとwaveLengthは中央値を基準に半分の長さから2倍の長さの波を作るといいと書いてあります。

下のgifはオリジナルのIsland11の波です。RGBをノーマル、Aをハイトマップとしたテクスチャを元に頂点を移動するだけというシンプルなアルゴリズムですが、このテクスチャの出来がいいのでとてもきれいです。

Copyright (c) 2011 NVIDIA Corporation.


波による頂点移動処理はドメインシェーダ内あり、そこをゲルストナー波に置き換えてみます。うねる波が表現できた半面、海らしい荒さが足りない気がします。

Copyright (c) 2011 NVIDIA Corporation.


そこで、Island11のオリジナルの波と合成してみると、それなりに見られるものになりました。結局ゲルストナー波単体できれいな絵はできず、さざ波や白波の表現も工夫していく必要がありそうです。

Copyright (c) 2011 NVIDIA Corporation.


次回は法線を求めてみます。

References:
Effective Water Simulation from Physical Models
Ocean Shader with Gerstner Waves
GERSTNER WAVE IMPLEMENTATION
DX11 Samples