GLSL Sandboxにマンデルブロ集合があり、面白そうだったのでちょっと使いやすそうに加工してからUE4のマテリアルにしてみました。
ループがあるのでCustomノードに書きます。発散の判定は絶対値が2を超えた時点とし、発散と判定されるまでの計算回数が多い程輝度が上がるようにしました。
探検できるように300倍に拡大した'Plane'にマテリアルを貼り付けます。
Spectator Pawnで探検します。
離れてみると分かりにくいですが、近くから輪郭部を見ると非常に輝度が高いのがわかります。集合の左側は特に輝度が高く、更に拡大しても複雑なパターンが現れてきそうです。
プロジェクトをgithubにアップしました。
Monday, November 27, 2017
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点を得ました。ここから得た法線と真上に向かうベクトルとのなす角を回転量とし、同様に法線と真上に向かうベクトルとの外積で回転軸を求めています。
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でタイミングまでプログラマが把握して指定しなければいけません。
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かポストプロセスくらいなので、場合分けは難しくないと思います。
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を使いたいならプラットフォーム毎に分岐しなければいけなさそうです。
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)とみなしてワールド空間に配置します。
次回は水面に物を浮かべてみます。
ゲルストナー波の法線を計算してみます。安直な方法としては、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
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
Labels:
C++,
DirectX11,
DirectX12,
Gerstner Wave,
GPU Gems,
HLSL,
Japanese,
Visual Studio
Monday, July 17, 2017
[UE4] JAXAのGioTIFFから実物大ランドスケープを生成する
JAXAが無償公開している全世界の標高データをUE4に読み込ませてみました。
標高データは http://www.eorc.jaxa.jp/ALOS/aw3d/index.htm からダウンロード可能です。
GeoTIFFという形式になっており、そのままではUE4では使えません。
gdal_translate.exeという変換プログラムを使って16bit PNG形式に変換します。
gdal_translateは、 http://www.gisinternals.com/ のものを使用しました。
北緯42度、東経140度のデータをUE4で使えるように16bit PNGに変換します。
-scaleというオプションを付けて、-32768~32767の範囲の値を0~65535の範囲にマッピングし直します。UE4ではどうやら32768をゼロとみなしており、-scale指定無しでははるか下方に地面が生成されてしまいます。一方、元データは符号付きのメートル単位の標高なので、32768を足してUE4に合わせました。
スケールの設定ですが、 せっかくなので標高を実物大にしてみます。https://wiki.unrealengine.com/Landscape_-_Sizes_and_Height_Guide
この資料によると、Zのスケールが100なら範囲が-256メートルから+256メートルになるそうです。Zのスケール1あたり2.56メートルですね。PNGに変換するときに-scaleオプションで標高の最大を32768にしたので、32768 / 2.56 = 12800 をZのスケールにすると実物大になります。
次に、XY方向を実物大にすることを考えてみます。上の資料によるとどうやらハイトマップの1ピクセルが1メートルに対応しているようです。JAXAの標高データは地球の1arcsecである約30メートル解像度なので、3000cmを掛ければY方向(南北)は実物大になります。X方向(東西)は北緯42度地点では1arcsecが赤道を1とした場合cos(42)にしないと大きさが合いませんが、UE4のランドスケープの仕様上XとYの方向の倍率を別々に指定できません。今回はX方向も3000にして我慢します。
これがほぼ実物大でインポートするScale設定になります。
マテリアルはレイヤーを用いず、標高で緑と青を塗り分ける適当なものです。
ところで、実物大になったゆえの問題が、100km四方に及ぶ広大な領域を持て余していまいち面白くないというのがあります。またUE4特有の問題として、座標値にして100万くらい(約10km)を超える場所では正常なレンダリングが保証されない問題があります。その大きさはソースコード上にHALF_WORLD_MAXというマクロで定義されています。
そこで、XYを1/10スケールである300にします。標高が強調されて地形がわかりやすくなり、面白みのある風景が出現しました。
マネキンを羊蹄山の山頂1898Mに配置し、標高が再現されていることが検証できました。
ALOS全球数値地表モデル (c)JAXA
http://www.eorc.jaxa.jp/ALOS/aw3d30/index_j.htm
標高データは http://www.eorc.jaxa.jp/ALOS/aw3d/index.htm からダウンロード可能です。
GeoTIFFという形式になっており、そのままではUE4では使えません。
gdal_translate.exeという変換プログラムを使って16bit PNG形式に変換します。
gdal_translateは、 http://www.gisinternals.com/ のものを使用しました。
北緯42度、東経140度のデータをUE4で使えるように16bit PNGに変換します。
gdal_translate.exe -of PNG -ot UInt16 -scale -32768 32767 0 65535 N042E140_AVE_DSM.tif
-scaleというオプションを付けて、-32768~32767の範囲の値を0~65535の範囲にマッピングし直します。UE4ではどうやら32768をゼロとみなしており、-scale指定無しでははるか下方に地面が生成されてしまいます。一方、元データは符号付きのメートル単位の標高なので、32768を足してUE4に合わせました。
スケールの設定ですが、 せっかくなので標高を実物大にしてみます。https://wiki.unrealengine.com/Landscape_-_Sizes_and_Height_Guide
この資料によると、Zのスケールが100なら範囲が-256メートルから+256メートルになるそうです。Zのスケール1あたり2.56メートルですね。PNGに変換するときに-scaleオプションで標高の最大を32768にしたので、32768 / 2.56 = 12800 をZのスケールにすると実物大になります。
次に、XY方向を実物大にすることを考えてみます。上の資料によるとどうやらハイトマップの1ピクセルが1メートルに対応しているようです。JAXAの標高データは地球の1arcsecである約30メートル解像度なので、3000cmを掛ければY方向(南北)は実物大になります。X方向(東西)は北緯42度地点では1arcsecが赤道を1とした場合cos(42)にしないと大きさが合いませんが、UE4のランドスケープの仕様上XとYの方向の倍率を別々に指定できません。今回はX方向も3000にして我慢します。
これがほぼ実物大でインポートするScale設定になります。
マテリアルはレイヤーを用いず、標高で緑と青を塗り分ける適当なものです。
ところで、実物大になったゆえの問題が、100km四方に及ぶ広大な領域を持て余していまいち面白くないというのがあります。またUE4特有の問題として、座標値にして100万くらい(約10km)を超える場所では正常なレンダリングが保証されない問題があります。その大きさはソースコード上にHALF_WORLD_MAXというマクロで定義されています。
そこで、XYを1/10スケールである300にします。標高が強調されて地形がわかりやすくなり、面白みのある風景が出現しました。
マネキンを羊蹄山の山頂1898Mに配置し、標高が再現されていることが検証できました。
ALOS全球数値地表モデル (c)JAXA
http://www.eorc.jaxa.jp/ALOS/aw3d30/index_j.htm
Wednesday, June 28, 2017
[HLSL] シェーダ内に定数を定義したつもりだが値が割り当てられない問題
HLSLでは関数の外に変数を値付きで記述ができますが、この宣言だけでは見た目に反して変数から正常に値を読み出すことができず、エラーも出ません。
C++側から値を変更するつもりが無いならstaticをつけることで定数になり、意図通り動作します。(constではないです)
staticを付けなかった場合、NSIGHT等で見ると暗黙のコンスタントバッファが生成されることがわかります。ところが肝心の値が代入されず、0で埋まっています。なぜ文法的に許容されるか不可解に思えるところですが、今は非推奨となったD3DX11Effectを使うと、暗黙のコンスタントバッファに宣言通りの値を代入してくれます。(そして、D3DX11Effect::Applyを呼ぶと内部でVSSetConstantBuffers、PSSetConstantBuffers等の呼び出しまでしてくれます)
コードをモダン化するためにD3DX11Effectを取り除く作業中に分かり難いバグに遭遇したら、暗黙のコンスタントバッファがないか確認してみると良いかもしれません。
コードをモダン化するためにD3DX11Effectを取り除く作業中に分かり難いバグに遭遇したら、暗黙のコンスタントバッファがないか確認してみると良いかもしれません。
Monday, February 27, 2017
[DX11] DXGI_FORMAT_R32_TYPELESSのTYPELESSとは?
CreateTexture2DでDXGI_FORMAT_D32_FLOAT型のデプスバッファを作る時に以下のようなエラーが発生する場合、DXGI_FORMAT_D32_FLOATの代わりにDXGI_FORMAT_R32_TYPELESSを指定してエラーを回避できます。
おそらく、DSVとSRVをそれぞれD32、R32として作るのでCreateTexture2Dではテクスチャを型が未定のストレージと規定して齟齬が起こらないようにする意図と思われます。
D3D11 ERROR: ID3D11Device::CreateTexture2D: The format (0x28, D32_FLOAT) cannot be bound as a ShaderResource or cast to a format that could be bound as a ShaderResource. Therefore this format cannot support D3D11_BIND_SHADER_RESOURCE. Specifiying the format R32_TYPELESS instead would solve this issue. [ STATE_CREATION ERROR #92: CREATETEXTURE2D_UNSUPPORTEDFORMAT]
つまり、デプスバッファとしてレンダリングに使った後、SRVとしてシェーダから参照させたいならCreateTexture2Dの時はDXGI_FORMAT_R32_TYPELESSにしなければいけません。そして、そのテクスチャからSRVとDSVを作る場合、CreateShaderResourceViewにはDXGI_FORMAT_R32_FLOAT、CreateDepthStencilViewにはDXGI_FORMAT_D32_FLOATをそれぞれ指定します。
おそらく、DSVとSRVをそれぞれD32、R32として作るのでCreateTexture2Dではテクスチャを型が未定のストレージと規定して齟齬が起こらないようにする意図と思われます。
Thursday, February 2, 2017
6年前のNVIDIA Direct3D SDK 11を今の環境でコンパイルする
NVIDIAの"Direct3D SDK 11"はIsland11を始めとした良質なサンプルのソースコードが入っているので時々いじってみたくなります。しかし古いサンプルなので、Visual Studio 2015とWindows 10でコンパイル+実行するには一手間掛ける必要があります。
- https://developer.nvidia.com/dx11-samplesからDirect3D SDK 11をダウンロードします。一行目の緑色の"here"をクリックします。
- D3DXが使われているので、DirectX SDKが必要です。https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812 から手に入れます。
- Windows 10でDirectX SDKを入れようとすると"S1023"エラーが発生します。https://support.microsoft.com/en-us/help/2728613/-s1023-error-when-you-install-the-directx-sdk-june-2010 に解決策があります。インストール前にMicrosoft Visual C++ 2010 x86 RedistributableとMicrosoft Visual C++ 2010 x64 Redistributableを消せば良いようです。
- Samples_2008.sln を開きます。Visual Studio 2015用にコンバートする過程でWarningが出ますが、特に問題はなさそうです。
ソリューション内の各プロジェクトに以下の変更を行います。
- *.fx, *.hlslはプロジェクトから除外しておきます。(Removeで除外、あるいはExcluded From Build設定)VS2015はこれらがプロジェクトにあるとコンパイルしようとしますが、シェーダーバイナリを使うプロジェクトがないのでその必要はありません。
- ConfigrationをDebugにするとd3dx11effectsd.libが無いと言われてビルドできません。'd'の無いd3dx11effects.libはNVIDIA Corporation\NVIDIA Direct3D SDK 11\Libの下にあり、Releaseにしておくとリンクできます。参考:https://www.gamedev.net/topic/602742-getting-nvidia-sdk-samples-to-compile/
- Additional Include Directoriesに;%DXSDK_DIR%\Includeを追加
- Additional Library Directoriesに;%DXSDK_DIR%\Lib\x86を追加
- __vsnwprintfが無いと言われるので、Additional Dependenciesにlegacy_stdio_definitions.libを追加
- Treat Warnings As ErrorsはNoにしておきます。DXGI_*系のマクロがWindowsSDKに取り込まれた結果、再定義されたと警告が出るようです。又は、C4005警告をDisable Specific Warningsに入れてもいいのですが、これ以外にも警告が多いので一括で許可したほうが簡単です。
- TerrainTessellation.cppで、powfとstd::maxのコンパイルが出来ないので、#include <algorithm>を追加
ただ、プロジェクトの数が多いので、一つ一つ設定するのは大変です。そこで、少々乱暴ですが以下のようにします。
- C:\ProgramData\NVIDIA Corporation\NVIDIA Direct3D SDK 11\Include\DXUT\Core にDirectX SDKのヘッダファイルを入れてしまう。
- C:\ProgramData\NVIDIA Corporation\NVIDIA Direct3D SDK 11\Lib にも同様に C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86 からライブラリファイルを持ってくる。
- NVIDIA Direct3D SDK 11\Include\DXUT\Core\DXUT.hに、以下の行を追加する。
#pragma comment(lib,"legacy_stdio_definitions.lib")
#pragma warning(disable:4005)
これで、大部分のプロジェクトはHLSLファイルを除外するだけでビルドに成功するようになります。
更に、Debugでビルドするには、d3dx11effectsd.lib、dxutd.lib、dxutoptd.libといった、いくつかのライブラリが存在しておらずリンクできません。これは、DirectX SDKのSamplesフォルダに含まれるいくつかのプロジェクトをDebugでビルドしたものです。ビルドしたlibファイルをNVIDIA SDKの下のLibフォルダに置けばリンクできるのですが、NVIDIAの開発者がファイル名をリネームして使っているようです。
更に、Debugでビルドするには、d3dx11effectsd.lib、dxutd.lib、dxutoptd.libといった、いくつかのライブラリが存在しておらずリンクできません。これは、DirectX SDKのSamplesフォルダに含まれるいくつかのプロジェクトをDebugでビルドしたものです。ビルドしたlibファイルをNVIDIA SDKの下のLibフォルダに置けばリンクできるのですが、NVIDIAの開発者がファイル名をリネームして使っているようです。
- Samples\C++\DXUT11\Core\DXUT_2010.slnからDXUT.libが出来ますが、DXUTd.libにリネームします。
- Samples\C++\DXUT11\Core\DXUTOpt_2010.slnからDXUTOpt.libが出来ますが、DXUTOptd.libにリネームします。
- Effects11_2010.slnをビルドしようとすると、static void* __cdecl operator new(size_t s, CDataBlockStore &pAllocator) という関数がERROR C2323"non-member operator new and delete functions may not be declared inline"というエラーになります。これは、staticをinlineに変更するとコンパイルできます。Effects11.libというファイルが出来ますが、d3dx11effectsd.libにリネームします。
Sunday, January 8, 2017
[Vulkan] VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMICが便利
VulkanのUniform Bufferには、VK_DESCRIPTOR_TYPE_UNIFORM_BUFFERとVK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMICの二種類がありますが、"DYNAMIC"はオフセットをコマンドバッファに一緒に積めるので便利です。
DirectX12やVulkanのような低レベルAPIでは、Constant buffer/Uniform buffer用に大きなGPU側バッファをまず確保しておいて、自前のアロケータで割り当てながら使うことになると思います。どのDraw Callがどのメモリ領域をConstant buffer/Uniform bufferとして使うかは毎フレーム変化するしかないので、Draw Call毎にGPUアドレスを渡すことになります。
それは、VK_DESCRIPTOR_TYPE_UNIFORM_BUFFERを使っても可能なのですが、GPUアドレスはdescriptor setに書き込んで渡す必要があるので、uniform bufferを使う回数分vkAllocateDescriptorSetsとvkUpdateDescriptorSetsでdescritor setも作ってあげる必要があります。
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMICを使うと、descritor setは1個だけ、大きなGPUバッファの先頭アドレスを書き込んだものを用意するだけで済みます。その代わりdraw call毎にvkCmdBindDescriptorSetsでUniform bufferをバインドするときにオフセットを指定します。複数のdescriptor setを管理する手間が省けるので便利です。
NVIDIAのドキュメントによると、NVIDIAのハードウェアで"Uniform Buffer Dynamic Binding"は速いのだそうです。
https://developer.nvidia.com/vulkan-shader-resource-binding
推測に過ぎませんが、NVIDIAのハードウェアではDirectX12の"Root Constant"が"Uniform Buffer Dynamic Binding"に相当するのかもしれません。
DX12 Do's And Don'tsのRoot Signaturesの項で"sit in the root"でバインドしたコンスタント/CBVが特にPixel Shaderで速いとされています。
https://developer.nvidia.com/dx12-dos-and-donts
SetGraphicsRootConstantBufferViewもDescriptor Heapを介さずにコマンドリストに直接GPUアドレスを乗せるという部分も、descriptor set1個を使いまわせるVK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMICに似ています。
DirectX12やVulkanのような低レベルAPIでは、Constant buffer/Uniform buffer用に大きなGPU側バッファをまず確保しておいて、自前のアロケータで割り当てながら使うことになると思います。どのDraw Callがどのメモリ領域をConstant buffer/Uniform bufferとして使うかは毎フレーム変化するしかないので、Draw Call毎にGPUアドレスを渡すことになります。
それは、VK_DESCRIPTOR_TYPE_UNIFORM_BUFFERを使っても可能なのですが、GPUアドレスはdescriptor setに書き込んで渡す必要があるので、uniform bufferを使う回数分vkAllocateDescriptorSetsとvkUpdateDescriptorSetsでdescritor setも作ってあげる必要があります。
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMICを使うと、descritor setは1個だけ、大きなGPUバッファの先頭アドレスを書き込んだものを用意するだけで済みます。その代わりdraw call毎にvkCmdBindDescriptorSetsでUniform bufferをバインドするときにオフセットを指定します。複数のdescriptor setを管理する手間が省けるので便利です。
NVIDIAのドキュメントによると、NVIDIAのハードウェアで"Uniform Buffer Dynamic Binding"は速いのだそうです。
https://developer.nvidia.com/vulkan-shader-resource-binding
推測に過ぎませんが、NVIDIAのハードウェアではDirectX12の"Root Constant"が"Uniform Buffer Dynamic Binding"に相当するのかもしれません。
DX12 Do's And Don'tsのRoot Signaturesの項で"sit in the root"でバインドしたコンスタント/CBVが特にPixel Shaderで速いとされています。
https://developer.nvidia.com/dx12-dos-and-donts
SetGraphicsRootConstantBufferViewもDescriptor Heapを介さずにコマンドリストに直接GPUアドレスを乗せるという部分も、descriptor set1個を使いまわせるVK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMICに似ています。
Subscribe to:
Posts (Atom)