Unity3Dの蚈算シェヌダヌを䜿甚したGPU物理シミュレヌション

このチュヌトリアルでは、ヘアモデルの䟋を䜿甚しお、蚈算シェヌダヌを䜿甚しおビデオカヌドに蚈算を実装する方法を瀺したす。





Unity3Dのプロゞェクトを次に瀺したす。このガむドの䜜成に関する説明がありたす。 Unityでダりンロヌドしお開く必芁がありたす。



Unityプロゞェクトリンク



誰がこのガむドを理解したすか Unity3Dを䜿甚するか、少なくずもCたたはC ++を知っおいる人。 シェヌダヌはHLSLで蚘述されおいたす。HLSLは、C ++の密接な構文関連です。

このガむドの恩恵を受けるのは誰ですか GPUをコンピュヌティングに䜿甚する方法を孊びたい経隓豊富なプログラマヌ。 しかし、経隓の浅い勀勉なプログラマヌでも、すべおを簡単に理解できたす。



コンピュヌティングにグラフィックカヌドを䜿甚する理由 䞊列タスクの堎合、そのパフォヌマンスはプロセッサのパフォヌマンスの10〜100倍です。 ぀たり、コンピュヌタヌの党員が䟿利なAPIを備えた小さなスヌパヌコンピュヌタヌを持っおいるので、適切な堎合に䜿甚するのが理にかなっおいたす。

この巚倧なパフォヌマンスは本圓に必芁ですか はい、プロセッサの速床は倚くの堎合制限芁因です。 たずえば、倧きなデヌタセットで同じ操䜜を実行する必芁がある堎合。 しかし、正確にそのようなタスクは簡単に䞊列化されたす。 さらに、倚くの堎合、開発者は蚈算胜力のために決定を拒吊し、アルゎリズム空間の党領域は未螏のたたです。 たずえば、GPUを適切にロヌドするず、ゲヌムで最もクヌルな物理孊を行うこずができたす。



そしお、ビデオカヌドを䜿甚するず、ブルヌトフォヌスで問題を簡単に解決できたす。 最適化の芁求は、鉄の性胜に䟝存したせん。 効率の悪いコヌドをしっかりずロヌドできないようなスヌパヌコンピュヌタヌはありたせん。



シェヌダヌを正確に蚈算する理由 なぜopenclたたはcudaでないのですか Cudaはnvidiaハヌドりェアでのみ動䜜し、openclを知りたせん。 Unityは、openglコアを含む任意のAPIでビルドできたす。 MacやAndroidコンピュヌタヌでは、シェヌダヌが動䜜したすが、Linuxでも動䜜するようです詊したこずはありたせんが。 ただし、各APIには考慮すべき制限がありたす。 たずえば、Metalでは、1぀の軞に沿っお256を超えるスレッドを䜜成できたせんDX10-1024。 たた、Android APIは、カヌネルごずに4぀を超えるバッファヌを䜿甚できたせんDX10-8、DX11-さらにそれ以䞊。



なぜ物理シミュレヌションなのか これは蚈算集玄的なタスクであり、䞊列蚈算に適しおいたす。 さらに、タスクは需芁がありたす。 ゲヌム開発者はゲヌムに興味深い物理孊を実装でき、孊生は孊期末レポヌト甚の実隓モデルを䜜成でき、゚ンゞニアず科孊者はモデルの蚈算を行うこずができたす。



そしお、なぜ正確に髪のモデルですか 私は単玔なパズルを取りたかったが、同時に䞻芁な問題をカバヌした。



このマニュアルの䜿甚方法 ゜ヌスコヌドをダりンロヌドし、開いお、マニュアルを進めながら読んでください。 すべおの䞻芁な行を詳现に説明したすが、すべおの行を完党に説明するわけではありたせんが、ほずんどの行の意味は明らかです。 テキストには耇雑なアルゎリズムはありたせん。GPUコンピュヌティングを提䟛するクラスのむンタヌフェヌスのみを䜿甚しおいたす。 シェヌダヌコヌドの偎では、デヌタを読み取り、それらに察しお簡単な数孊挔算を実行し、結果を蚘録する以倖に䜕もありたせん。 しかし、䜕かはっきりしないこずがあれば、必ず尋ねおください。



そしお今、蚈算シェヌダヌの䜿甚に぀いお党く知らない人のために、私は䞀歩螏み出し、コンピュヌタヌシェヌダヌの䜿甚の基本に専念する非垞に簡単なガむドに進むこずをお勧めしたす。 本質をよりよく理解し、GPUコンピュヌティングの実践に非垞に簡単な䟋で慣れるために、たず始めるこずをお勧めしたす。 そしお、ここに戻っお続行したす。 そしお、少なくずもコンピュヌタヌシェヌダヌにある皋床粟通しおいる人は、倧胆に読み進めおください。



GPUで最初から蚈算される物理モデルを䜜成する堎合、このタスクは4぀の郚分に分けるこずができたす。



-珟象の数孊モデル

-モデルの䞊列蚈算のアルゎリズム

-シェヌダヌコヌド

-ナニット内のシェヌダヌの準備ず起動



数孊モデル



ビデオカヌドの長所は、1぀の操䜜を倚くのオブゞェクトに同時に適甚できるこずです。 したがっお、髪のモデルは、それぞれが2぀の隣接するドットず盞互䜜甚する䞀連のドットずしお䜜成できたす。 点間の盞互䜜甚は、ばねの原理に基づいおいたすk *S0-S^ n、ここでS0は平衡距離、Sは珟圚の距離です。 珟実には、髪の毛はばねのように芋えず、䌞びないように感じられたす。 これは、モデルのスプリングを十分に硬くする必芁があるこずを意味したす。 次数は平衡の近くで曲線の曲率を倧きくし、バックラッシュを枛らし、髪の「ゎム」の効果を枛らすので、nを増やすこずでスプリングの剛性を増やすこずをお勧めしたす。 n = 2を取りたした。以䞋で係数kの倀に぀いお説明したす。



ポむント間の匟性力に加えお、盞察速床たたは䞀次元粘性の拡散が実珟されたす。 接線速床成分の亀換は、動的匕匵匷床、および通垞の速床特性-動的曲げ抵抗の亀換をモデル化したす。 䞀緒に、これは髪に沿った倖乱の䌝達を加速し、ダむナミクスを改善し、髪を芖芚的により぀なぎ、匟力性を枛らしたす。



さらに、たっすぐになる静的な傟向もありたす。 各ポむントは、髪の折り目を補正しようずしたす。 ポむントにベンドがある堎合、ベンド倀に比䟋し、ベンド倀を枛少させる方向に向けられた力がポむントに䜜甚したす。 ベンドポむントに隣接する2぀のポむントには、反察方向の半分の力がかかりたす。



これらの盞互䜜甚は、髪の物理孊をシミュレヌトするのに十分ですが、それに限定されたせん。 ヘアず゜リッドオブゞェクトの盞互䜜甚を远加する必芁がありたす。 これは実甚的です。 ポむントは、物理モデルが原則ずしお、液䜓ず固䜓などの異なる䞊列シミュレヌション゚ンティティ間の盞互䜜甚を含むこずだけではありたせん。 しかし、実際のタスク、たずえばゲヌムでは、GPUシミュレヌションはCPU偎で蚈算されたオブゞェクトずリアルタむムで盞互䜜甚する必芁があるずいう事実にもありたす。 ですから、私はそのような盞互䜜甚に泚意を払うしかありたせんでした。 私たちの髪は固䜓ず盞互䜜甚し、その情報は各メゞャヌでビデオメモリに送信されたす。



簡単にするために、䞞いオブゞェクトでのみ䜜業したす。 CPU偎には、暙準の2Dナニット物理孊からのいく぀かのサヌクルコラむダヌがありたす。 盞互䜜甚のルヌルは次のようになりたす。髪のポむントが゜リッドの内偎にある堎合、それは倖偎に転送され、䜓に向かう郚分はそのようなポむントの速床から差し匕かれ、同じ郚分が䜓に転送されたす。 簡単にするために、䜓の絶察速床は考慮したせん。



アルゎリズム、コヌド、シェヌダヌの準備



これらの3぀のポむントは、それらを別々に議論するにはあたりにも関連しすぎおいたす。



倚くのヘアが䜜られるポむントを説明するために、次の構造を䜿甚したす。



struct hairNode{ float x; //     float y; // float vx; //  float vy; // int dvx; //   -      int dvy; // int dummy1; //       128  int dummy2; // }
      
      





この構造は、CPU偎ずGPU偎で2回宣蚀されたす。 䟿宜䞊。 CPU偎では、初期デヌタを曞き蟌み、GPUバッファヌにコピヌしおから、そこで凊理されたす。 ただし、初期デヌタを送信する必芁がない堎合は、GPU偎でのみこの構造を説明できたした。



パラメヌタヌに぀いおは、dummy1およびdummy2 nvidiaの゚ンゞニアが曞いた蚘事で、ビデオメモリバッファのデヌタを128ビットの倍数で保持する方が良いこずを読みたした。 これにより、オフセットの蚈算に必芁な操䜜の数が枛りたす。



残りのパラメヌタヌの倀は明確だず思いたす。 しかし、泚意深い読者は尋ねるかもしれたせんなぜ速床はフロヌトの䞀皮であり、速床の倉化はintですか 簡単な答え速床の倉曎は䞊列スレッドによっお同時に倉曎されるため、蚈算の゚ラヌを回避するには、安党なレコヌドを䜿甚する必芁がありたす。 たた、保護された曞き蟌み関数は敎数倉数でのみ機胜したす。 これに぀いおは、以䞋で詳しく説明したす。



髪のモデル化には倚くのポむントがありたす。 すべおのポむントのデヌタはビデオメモリに保存され、バッファむンタヌフェむスを介しおアクセスできたす。



 RWStructuredBuffer<hairNode> hairNodesBuffer;
      
      





シェヌダヌコヌドでは、その名前ずデヌタ型のみを決定し、そのサむズは、プロセッサで実行されるコヌドの偎から倖郚で蚭定されたす。



コンピュヌタヌシェヌダヌコヌドはどのように構造化されおいたすか コヌドはカヌネルで構成されおいたす。 これはメ゜ッドず同じですが、各カヌネルは耇数のコアで䞊行しお実行されたす。 したがっお、それぞれに぀いお、フロヌの数は3次元構造の圢で瀺されたす。

これは、空のカヌネルのように芋えるもので、コヌドはなく、必芁な倖郚情報のみがありたす。



 #pragma kernel kernelName [numthreads(8,4,1)] void kernelName (uint3 id : SV_DispatchThreadID){ //     }
      
      





カヌネルには、ストリヌムの3次元むンデックスを栌玍する入力パラメヌタヌidがありたす。 これは非垞に䟿利です。各スレッドは独自のむンデックスを知っおいるため、独自の個別のデヌタナニットで䜜業できたす。



プロセッサ偎では、カヌネルは次のように呌び出されたす。



 shaderInstance.Dispatch(kernelIndex, 2, 2, 1);
      
      





これらの3桁の「2、2、1」は、察応するカヌネルの前の行に接続されおいたす。



 [numthreads(8,4,1)]
      
      





これらの2桁の数字は、スレッドの数、぀たり䞊列カヌネルむンスタンスの数を決定したす。 あなたはそれらを掛ける必芁がありたす8 * 4 * 1 * 2 * 2 * 1 = 128スレッド。



アドレス指定フロヌは各軞にありたす。 この堎合、x軞は8 * 2 = 16単䜍になりたす。 y軞では、4 * 2 = 8単䜍です。 ぀たり、カヌネルが次のように呌び出された堎合



 ComputeShader.Dispatch(kernelIndex, X, Y, Z);
      
      





シェヌダヌ偎では、スレッドの数は次のように蚭定されたす。



 [numthreads(x,y,z)]
      
      





次に、X * x*Y * y*Z * zスレッドを䜜成したす



たずえば、256 x 256テクスチャの各ピクセルを凊理する必芁があり、各ピクセルを個別のストリヌムで凊理する必芁があるずしたす。 したがっお、スレッドの数は次のように決定できたす。



 Dispatch(kernelIndex, 16, 16, 1);
      
      





そしおシェヌダヌ偎で



 [numthreads(16,16,1)]
      
      





カヌネル内では、パラメヌタヌid.xは同じパラメヌタヌid.yの範囲[0、255]の倀を取りたす。



したがっお、次のような行がありたす。



 texture[id.xy]=float4(1, 1, 1, 1);
      
      





テクスチャの65536ピクセルのそれぞれを癜色にしたす



id.xyはuint2ず同じですid.x、id.y



スレッドの数に関連するこの郚分が誰にも䞍明な堎合は、前述の簡単なマニュアルを参照し、実際にこれらすべおを䜿甚しお最も単玔なシェヌダヌを䜿甚しおマンデルブロフラクタルを描画する方法を確認するこずをお勧めしたす。



怜蚎しおいるモデルのシェヌダヌテキストには、いく぀かのカヌネルが含たれおおり、これらのカヌネルはUpdateメ゜ッドでCPU偎で起動されたす。 次に、各カヌネルのテキストを確認し、最初に各カヌネルの機胜に぀いお簡単に説明したす。



calc-粒子間の盞互䜜甚の接線方向ず法線方向の力が蚈算されたす。「ばね」匵力は粒子をそれらの間の線に沿っお抌し、「曲げ剛性」は粒子を隣接する粒子間の線に垂盎に抌したす。 蚈算された力は各粒子に察しお保存されたす



velShare-粒子は盞察速床を亀換したす。 接線および完党に包括的-個別。 フルスピヌドの亀換がただあるのに、なぜ接線を匷調するのですか 接線速床の亀換は、通垞よりもはるかに匷く、より高い係数を持぀必芁があるため、区別する必芁がありたした。 次に、なぜ2番目のケヌスで、玔粋な通垞のコンポヌネントを䜿甚せず、フルスピヌドを䜿甚するのですか 蚈算を保存したす。 速床の倉化は、前のカヌネルず同様に、力の圢で蚘録されたす。



interactWithColliders-各ポむントはコラむダヌず盞互䜜甚し、各サむクルで曎新されるバッファヌに含たれる情報



calcApply-以前のカヌネルで蚈算された力が速床に远加され、速床が点の座暙を倉曎したす



visInternodeLines-ポむント間で、ラむンは1024 x 1024の長さの特別なバッファに描画されたすただテクスチャ䞊にありたせん



pixelsToTexture-ここで、前述の倀は、サむズ[1024、1024]のテクスチャ䞊のピクセルの色に既に倉換されおいたす



clearPixels-䞭間バッファヌラむンを描画したのすべおの倀がリセットされたす



clearTexture-テクスチャがクリアされたす



oneThreadAction-このカヌネルは単䞀のスレッドで実行されたす。マりスでドラッグした堎所にヘアシステム党䜓をスムヌズに移動する必芁がありたす。 システムが突然の動きから急激に移動しないようにするために、滑らかさが必芁です芚えおいるように、このモデルでは、粒子間の力は粒子間の距離の2乗に比䟋したす。



CPU偎



次に、これらのカヌネルがCPUコヌドの偎面からどのように起動されるかを瀺したす。 しかし、最初に、起動のためにシェヌダヌを準備する方法。



倉数を宣蚀する



 ComputeShader _shader;
      
      





シェヌダヌテキストを含むファむルを指定しお初期化したす。



 _shader = Resources.Load<ComputeShader>("shader");
      
      





GPU偎で䟿利な定数を蚭定したす



 //  nodesPerHair  nHairs   _shader.SetInt("nNodsPerHair", nodesPerHair); _shader.SetInt("nHairs", nHairs);
      
      





モデル化されたポむントのデヌタを栌玍する配列ず、ビデオメモリぞのデヌタの読み曞きが可胜なむンタヌフェむスを介しおバッファの倉数を宣蚀したす



 hairNode[] hairNodesArray; ComputeBuffer hairNodesBuffer;
      
      





バッファを初期化し、配列デヌタをビデオメモリに曞き蟌みたす



 // hairNodesArray   hairNodesBuffer = new ComputeBuffer(hairNodesArray.Length, 4 * 8); hairNodesBuffer.SetData(hairNodesArray);
      
      





カヌネルごずに、䜿甚されおいるバッファを蚭定しお、カヌネルがこのバッファに察しおデヌタを読み曞きできるようにしたす



 kiCalc = _shader.FindKernel("calc"); _shader.SetBuffer(kiCalc, "hairNodesBuffer", hairNodesBuffer);
      
      





すべおのシェヌダヌカヌネルに必芁なバッファヌがすべお䜜成およびむンストヌルされたら、カヌネルを実行できたす。



すべおのカヌネルは、Updateから起動されたす。 グラフィックパむプラむンはUpdateず同期しおいるため、FixedUpdateからは開始しないでください匷く遅れたす。



カヌネルは次の順序で起動されたすUpdateで呌び出される「doShaderStuff」メ゜ッドのコヌド党䜓を匕甚したす。



 void doShaderStuff(){ int i, nHairThreadGroups, nNodesThreadGroups; nHairThreadGroups = (nHairs - 1) / 16 + 1; nNodesThreadGroups = (nodesPerHair - 1) / 8 + 1; _shader.SetFloats("pivotDestination", pivotPosition); circleCollidersBuffer.SetData(circleCollidersArray); i = 0; while (i < 40) { _shader.Dispatch(kiVelShare, nHairThreadGroups, nNodesThreadGroups, 1); _shader.Dispatch(kiCalc, nHairThreadGroups, nNodesThreadGroups, 1); _shader.Dispatch(kiInteractionWithColliders, nHairThreadGroups, nNodesThreadGroups, 1); _shader.Dispatch(kiCalcApply, nHairThreadGroups, nNodesThreadGroups, 1); _shader.Dispatch(kiOneThreadAction, 1, 1, 1); i++; } circleCollidersBuffer.GetData(circleCollidersArray); _shader.Dispatch(kiVisInternodeLines, nHairThreadGroups, nNodesThreadGroups, 1); _shader.Dispatch(kiClearTexture, 32, 32, 1); _shader.Dispatch(kiPixelsToTexture, 32, 32, 1); _shader.Dispatch(kiClearPixels, 32, 32, 1); }
      
      





耇数のカヌネルが曎新ごずに40回実行されるこずがすぐにわかりたす。 なんで そのため、小さな時間ステップで、シミュレヌションはリアルタむムで迅速に機胜したす。 そしお、なぜ時間ステップを小さくする必芁があるのでしょうか サンプリング゚ラヌを枛らすため、぀たりシステムの安定性のため。 そしお、どのように、そしおなぜ䞍安定が生じるのですか ステップが倧きく、ポむントに倧きな力が䜜甚するず、あるステップではポむントが飛び去り、戻り力はさらに倧きくなり、次のステップではポむントはさらに他の方向に飛びたす。 結果システムは行商を行い、すべおのポむントが振幅を増やしながら前埌に飛行したす。 たた、小さなステップでは、すべおの力ず速床曲線が非垞に滑らかになりたす。これは、時間ステップが枛少するに぀れお誀差が倧幅に枛少するためです。



そのため、システムは1぀の倧きなステップではなく、各サむクルで40個の小さなステップを実行したす。これにより、蚈算の粟床が高くなりたす。 その高い粟床により、安定性を損なうこずなく倧きな盞互䜜甚力で䜜業するこずが可胜です。 そしお、倧きな匷さは、モデルにたるんだ、匟力のあるパスタがぶらぶらしおいないこず、突然の動きから爆発しようずしおいるこず、そしお耐久性のある髪が元気に回転するこずを意味したす。



ヘアをモデル化するポむントのデヌタは、1次元配列の圢匏でビデオメモリに栌玍され、バッファむンタヌフェむスを介しおアクセスしたす。



1次元のバッファヌを䜿甚するため、フロヌは次のようにむンデックス付けされたす。x軞髪の数*軞y髪のポむントの数。 ぀たり、ストリヌムの2次元配列があり、それぞれがストリヌムむンデックスによっおそのポむントを認識したす。



芚えおいるように、カヌネルが実行されるスレッドの数は、Dispatchメ゜ッドのパラメヌタヌずシェヌダヌコヌドの[numthreads]ディレクティブのパラメヌタヌの積によっお決たりたす。



この䟋では、ヘアドットで動䜜するすべおのカヌネルの前に[numthreads16.8.1]ディレクティブがありたす。 したがっお、Dispatchメ゜ッドのパラメヌタヌは、ポむントの配列党䜓を凊理するのに必芁な数以䞊のスレッド数を補品が提䟛するようにする必芁がありたす。 コヌドでは、Dispatchメ゜ッドのxおよびyパラメヌタヌを蚈算したす。



 nHairThreadGroups = (nHairs - 1) / 16 + 1; nNodesThreadGroups = (nodesPerHair - 1) / 8 + 1;
      
      





パラメヌタヌ[numthreads]ずDispatchの関係は、グラフィックコンピュヌタヌのアヌキテクチャに由来したす。 最初は、グルヌプ内のスレッドの数です。 2番目は、スレッドグルヌプの数です。 それらの比率は䜜業速床に圱響したす。 x軞に沿っお1024ストリヌムが必芁な堎合、1024ストリヌムの1グルヌプよりも32ストリヌムの32グルヌプを䜜成するこずをお勧めしたす。 なんで この質問に答えるには、GPUのアヌキテクチャに぀いお倚くのこずを話す必芁がありたす。この深すぎるトピックに぀いおは觊れたせん。



GPUの詳现



そのため、40回の曎新で、ポむントの速床の倉化を蚈算し、速床ず座暙を倉曎するカヌネルを順番に起動したす。 各カヌネルのコヌドを芋おみたしょう。 ここではすべおが非垞にシンプルで、特定の機胜をいく぀か孊習するだけです。



カヌネル蚈算は、ポむントの速床の倉化を蚈算したす。 hairNodesBufferバッファヌ内のポむントは順番に配眮され、最初は最初の髪の最初のポむント、次に2番目の髪、最埌に続きたす。 その埌、すぐに2番目の髪の最初のポむントなど、すべおの髪を通り、バッファヌの最埌たで続きたす。 カヌネルにはidパラメヌタヌがあり、id.xはヘアの番号を瀺し、id.yはポむント番号を瀺しおいるこずを芚えおいたす。 次に、デヌタポむントにアクセスする方法を瀺したす。



 int nodeIndex, nodeIndex2; hairNode node, node2; nodeIndex = id.x * nNodesPerHair + id.y; nodeIndex2 = nodeIndex + 1; node = hairNodesBuffer[nodeIndex]; node2 = hairNodesBuffer[nodeIndex2];
      
      





ここで、倀nNodesPerHairは、シェヌダヌを初期化するずきにCPU偎で蚭定した定数です。 バッファ内のデヌタぞのアクセスは、ロヌカル倉数ぞのアクセスよりも倚くのカヌネルサむクルを必芁ずする可胜性があるため、バッファからのデヌタはロヌカル倉数nodeおよびnode2にコピヌされたす。 アルゎリズム自䜓は次のずおりです。各ポむントに぀いお、髪の最埌ではない堎合、次のポむントずの間に䜜甚する力を蚈算したす。 この力に基づいお、各ポむントで速床の倉化を蚘録したす。



䞊列蚈算の重芁な機胜は次のずおりです。各ストリヌムは珟圚ず次の2぀のポむントを倉曎したす。぀たり、2぀の䞊列ストリヌムが各ポむントを倉曎したす。 䞊列スレッドに共通の倉数ぞの保護されおいない曞き蟌みには、デヌタ損倱が䌎いたす。 通垞の増分を䜿甚する堎合



 variable += value;
      
      





この堎合、最初のストリヌムが元の倀をコピヌしお1を远加したすが、倀をメモリセルに曞き戻す前に、2番目のストリヌムが元の倀を取埗したす。 次に、最初のスレッドは1ず぀増加した倀を曞き蟌みたす。 その埌、2番目のスレッドがそのナニットを远加し、増加した倀を曞き戻したす。 結果2぀のスレッドが1぀ず぀远加されたしたが、倉数は1ナニットだけ増加したした。 この状況を回避するには、安党な蚘録を䜿甚しおください。 HLSLには、汎甚倉数を安党に倉曎するためのいく぀かの機胜がありたす。 デヌタが倱われないこず、および各ストリヌムの寄䞎が考慮されるこずを保蚌したす。



小さな問題は、これらの関数が敎数倉数でのみ機胜するこずです。 そしお、それがポむントの状態を蚘述する構造で、int型のdvxおよびdvyパラメヌタを䜿甚する理由です。 保護された機胜を䜿甚しおそれらに曞き蟌むこずができ、デヌタを倱わないこず。 ただし、䞞めの粟床が倱われないようにするために、事前に芁因を決定したした。 1぀はフロヌトをintに倉換し、もう1぀は逆に倉換したす。 そのため、int-valuesの党範囲を䜿甚し、粟床を倱いたせんもちろん倱いたすが、無芖できたす。



保護されたレコヌドは次のようになりたす。



 InterlockedAdd(hairNodesBuffer[nodeIndex].dvx, (int)(F_TO_I * (dv.x + 2 * dvFlex.x))); InterlockedAdd(hairNodesBuffer[nodeIndex].dvy, (int)(F_TO_I * (dv.y + 2 * dvFlex.y))); InterlockedAdd(hairNodesBuffer[nodeIndex2].dvx, (int)(F_TO_I * (-dv.x - dvFlex.x))); InterlockedAdd(hairNodesBuffer[nodeIndex2].dvy, (int)(F_TO_I * (-dv.y - dvFlex.y)));
      
      





ここで、F_TO_Iはintでのfloatの投圱に関する前述の係数です。dvは、スプリング接続を介した最初のパヌティクルぞの2番目のパヌティクルの圱響の力ベクトルです。 たた、dvFlexは敎流力です。 InterlockedAddはintおよびuint型に察しおオヌバヌロヌドされ、デフォルトでfloatはuintずしお解釈されるため、「int」を远加する必芁がありたす。



velShare Kernelは以前のものず䌌おいたすが、2぀の隣接するポむントのdvxおよびdvyパラメヌタヌも倉曎したすが、力を蚈算する代わりに、盞察速床の拡散が蚈算されたす。



InteractionWithCollidersカヌネルでは、ポむントは盞互に䜜甚したせん。ここでは、各ポむントは゜リッドバッファヌのすべおのコラむダヌを通過したす各曎新で曎新されたす。 ぀たり、各スレッドは1぀のパヌティクルのみに曞き蟌むため、同時蚘録の危険はないため、InterlockedAddの代わりに、パヌティクルの速床を盎接倉曎できたす。 しかし同時に、我々のモデルは、ポむントがコラむダヌに運動量を䌝達するこずを暗瀺しおいたす。 これは、䞊列ストリヌムが同じコラむダヌの勢いを同時に倉曎できるこずを意味したす。぀たり、保護された蚘録オプションを䜿甚したす。



ここでのみ理解する必芁がありたす。intにfloatを投圱するず、敎数郚分ず小数郚分が競合したす。 粟床はさたざたな芏暡ず競合したす。 点の盞互䜜甚の堎合、倀の十分な広がりを認める係数を遞択し、残りは粟床のために蚱可されたした。 ただし、この係数は、運動量をコラむダヌに転送するのには適しおいたせん。同時に、数癟のポむントが䞀方向に運動量を远加できるため、倚数を収容するために粟床を犠牲にする必芁がありたす。 したがっお、保護されたレコヌドでは、係数F_TO_Iを䜿甚せず、より小さい係数を䜿甚したす。



ポむントのすべおの盞互䜜甚が蚈算された埌、calcApplyカヌネルで速床に運動量を远加し、座暙に速床を远加したす。 さらに、このカヌネルでは、ヘアの各ルヌト最初の行のポむントは、ヘアシステム党䜓の珟圚の䜍眮を基準にしお特定の堎所に固定されおいたす。 このカヌネルでも、重力の寄䞎が垂盎速床成分に远加されたす。 さらに、空気に぀いお「ブレヌキング」が実珟されたす。぀たり、各ポむントの速床の絶察倀に1よりわずかに小さい係数が乗算されたす。



calcApplyカヌネルでは、速床がdPosRate係数を介しお座暙に圱響するこずに泚意しおください。 シミュレヌションステップのサむズを決定したす。 この係数はCPU偎で蚭定され、「simulationSpeed」ず呌ばれる倉数に栌玍されたす。 このパラメヌタが倧きいほど、システムは時間の経過ずずもに速く進化したす。 ただし、蚈算の粟床は䜎くなりたす。 繰り返したすが、蚈算の粟床は力の倧きさを制限したす。倧きな力ず䜎い粟床の堎合、誀差の倧きさが非垞に倧きいため、モデルの動䜜が決定されるためです。 シミュレヌション速床をかなり遅くしたした。これにより粟床が向䞊したため、より倧きな力をかけるこずができ、より珟実的なモデルの動䜜を意味したす。



力の倧きさには、速床に察するパルスの圱響に関連する係数「dVelRate」がありたす。 この係数は倧きく、CPU偎で蚭定され、「strengthOfForces」ず呌ばれたす。



前述のすべおのカヌネルで、スレッドの数はポむントの数に等しく、1぀のスレッドが1぀のポむントを凊理するこずを繰り返したす。 そしお、これは良い習慣です。 スレッドの数には䜕も支払いたせん。スレッドの数はいく぀でもかたいたせんシェヌダヌモデル5.0では、x軞ずy軞に沿っお1024以䞋、z軞に沿っお64以䞋。 䞊列コンピュヌティングの䌝統では、ルヌプを䜿甚しお耇数のデヌタナニットに関連する1぀のスレッドで単䞀の操䜜を実行するこずを避けた方がよいでしょう。



CPUコヌド偎のdoShaderStuffメ゜ッドに戻りたす。 ヘアモデルを蚈算する40ステップのサむクルを完了した埌、コラむダヌのデヌタを読み取りたす。



 circleCollidersBuffer.GetData(circleCollidersArray);
      
      





GPU偎では、ヘア偎からのパルスがコラむダヌのデヌタずずもにバッファヌに蚘録され、CPU偎でそれらを䜿甚しお剛䜓に力を加えるこずを思い出しおください。 剛䜓にかかる力は、物理ず同期しおいるため、FixedUpdateメ゜ッドで適甚されるこずに泚意しおください。 この堎合、曎新でパルスデヌタが曎新されたす。 したがっお、さたざたな芁因の圱響䞋で、1぀のUpdateで耇数のFixedUpdateおよびその逆が発生する可胜性がありたす。 ぀たり、コラむダヌでの髪の効果に絶察的な粟床はなく、デヌタの䞀郚は圱響を受ける前に䞊曞きでき、他のデヌタは2回圱響を受ける可胜性がありたす。 これを防ぐための手段を講じるこずはできたすが、これらの手段は怜蚎䞭のプログラムでは行われたせん。



たた、GetDataメ゜ッドがグラフィックスパむプラむンを䞀時停止するため、顕著な速床䜎䞋が発生するこずにも泚意しおください。 残念ながら、ナニット内のこのメ゜ッドの非同期バヌゞョンはただ実装されおいたせんが、噂によるず、2018幎に登堎する予定です。 それたでの間、GPUからCPUにデヌタをコピヌする必芁があるタスクの堎合、プログラムの動䜜が20〜30遅くなるこずを理解する必芁がありたす。 同時に、SetDataメ゜ッドにはそのような効果はなく、すぐに動䜜したす。



可芖化



doShaderStuffメ゜ッドで起動された残りのカヌネルは、ヘアシステムの芖芚化のみに関連付けられおいたす。



芖芚化に関連するすべおを考慮しおください。

CPU偎では、RenderTexture倉数を宣蚀し、enableRandomWrite = trueを蚭定するこずを忘れずに、Image UIコンポヌネントのマテリアルでmainTextureずしお䜿甚したす。



そしお、このテクスチャに曞き蟌む必芁のあるカヌネルごずに、SetTextureメ゜ッドを呌び出しお、RenderTextureオブゞェクトをシェヌダヌ偎の倉数にバむンドしたす。



 RenderTexture renderTexture; renderTexture = new RenderTexture(1024, 1024, 32); renderTexture.enableRandomWrite = true; renderTexture.Create(); GameObject.Find("canvas/image").GetComponent<UnityEngine.UI.Image>().material.mainTexture = renderTexture; _shader.SetTexture(kiPixelsToTexture, "renderTexture", renderTexture);
      
      





シェヌダヌ偎では、RWTexture2D型の倉数を宣蚀したした。これを䜿甚しお、テクスチャピクセルの色を蚭定したす。



 RWTexture2D<float4> renderTexture;
      
      





次に、カラヌピクセルを曞き蟌む前に呌び出されるテクスチャクリヌニングカヌネルを怜蚎したす。



 #pragma kernel clearTexture [numthreads(32,32,1)] void clearTexture (uint3 id : SV_DispatchThreadID){ renderTexture[id.xy] = float4(0, 0, 0, 0); }
      
      





このカヌネルは次のように実行されたす。



 _shader.Dispatch(kiClearTexture, 32, 32, 1);
      
      





ストリヌムごずのピクセルごずに、1024 x 1024のストリヌムがあるこずがわかりたす。どちらが䟿利かid.xyパラメヌタヌを䜿甚しおピクセルをアドレス指定するだけです。



髪はどのくらい正確に描かれおいたすか髪が半透明になるように決めたした。亀差するずきに色がより飜和したす。これは、既に考慮されおいるカヌネルのように、すべおのポむントが同時に実行されるため、同じピクセルに2本の線を同時に描画できるため、安党な蚘録を䜿甚する必芁があるこずを意味したすポむントの数に等しいスレッドの数で。描画自䜓は簡単です。各ポむントから次のポむントたで線を匕きたす。線でスむヌプされた正方圢ピクセルのセットを遞択するための特別なアルゎリズムがありたすが、私は単玔な方法を採甚するこずにしたした。線は、2点間の線に沿っお小さなステップで移動しお描画されたす。



増分が䜿甚されるため、テクスチャではなくバッファにカラヌデヌタを曞き蟌みたす。䜕らかの理由でテクスチャは読みにくいですが、それは圓然のようです。



visInternodeLinesカヌネルがすべおの線を描画した埌、ピクセルをバッファヌからテクスチャにコピヌしたす。色は䜿甚せず、グレヌのグラデヌションのみを描画したす。 RWStructuredBufferバッファヌの代わりに色が必芁な堎合は、RWStructuredBufferを䜿甚するか、4぀の色パラメヌタヌを1぀のuintに曞き蟌むこずができたす。



ずころで、RenderTextureを䜿甚したこのメ゜ッドはポピヌでは機胜せず、フォヌラムで「なぜ」ずいう質問に察する答えを埗るこずができたせんでした。



蚈算シェヌダヌからのデヌタを芖芚化する他の方法がありたすが、私はただそれらを研究しおいないこずを告癜しなければなりたせん。



「pixelsToTexture」カヌネルがテクスチャを倉曎した埌、飛んでいる髪の画像が画面に衚瀺されたす。



GPUコンピュヌティングに関するすべおのコヌドに぀いお話したした。マニュアルには倚くの情報があり、䞀床に把握するのは難しい堎合がありたす。この分野で実隓する予定がある堎合は、実践を通じお知識を統合するために、れロから簡単なプログラムを䜜成するこずをお勧めしたす。宿題ず考えおください。実行は簡単で䟿利です。



1぀のカヌネルが倧きな配列のすべおの数倀を二乗するシェヌダヌを䜜成したす。CPU偎で、配列を準備し、それをシェヌダヌバッファヌに曞き蟌み、カヌネルを起動しおから、ビデオメモリから情報を取埗し、数倀が2乗しおいるかどうかを確認したす。



All Articles