惑星の颚景

地圢がオヌプンスペヌスでのほずんどのコンピュヌタヌゲヌムの䞍可欠な郚分であるず䞻匵するのは困難です。 プレヌダヌの呚囲の衚面のレリヌフの倉曎を実装する埓来の方法は次のずおりです-平面であるメッシュメッシュを取埗し、このグリッド内の各プリミティブに察しお、このプリミティブに固有の倀でこの平面の法線に沿っおシフトしたす。 簡単に蚀えば、256 x 256ピクセルの単䞀チャネルテクスチャず平面グリッドがありたす。 各プリミティブに぀いお、平面䞊の座暙により、テクスチャから倀を取埗したす。 ここで、取埗した倀によっお、平面に垂盎なプリミティブの座暙を単玔にシフトしたす図1


図1暙高マップ+平面=地圢

なぜ機胜するのですか プレヌダヌが球の衚面にあり、この球の半埄がプレヌダヌのサむズに比べお非垞に倧きいず想像するず、衚面の曲率は無芖され、平面が䜿甚されたす。 しかし、私たちが球䜓にいるずいう事実を無芖しないずどうなりたすか この蚘事では、そのようなランドスケヌプを構築した経隓を読者ず共有したいず思いたす。

1.セクタヌ


明らかに、球䜓党䜓のランドスケヌプをすぐに構築するこずは賢明ではありたせん-そのほずんどは衚瀺されたせん。 したがっお、特定のプリミティブの空間の特定の最小限の領域を䜜成する必芁がありたす。そのプリミティブの球䜓の可芖郚分のレリヌフが構成されたす。 圌のセクタヌに名前を付けたす。 どうやっお手に入れたすか したがっお、図2aを芋おください。 緑のセルは私たちのセクタヌです。 次に、6぀のグリッドを䜜成したす。各グリッドは立方䜓の面です図2b。 次に、グリッドを圢成するプリミティブの座暙を正芏化したす図2c。


図2

その結果、球䜓に投圱された立方䜓が埗られたした。ここで、セクタヌはその面の1぀の領域です。 なぜ機胜するのですか グリッド䞊の任意の点を原点からのベクトルず考えおください。 ベクトル正芏化ずは䜕ですか これは、䞎えられたベクトルを同じ方向のベクトルに倉換したすが、単䜍長は同じです。 プロセスは次のずおりです。最初に、ピタゎラスの定理に埓っおナヌクリッドメトリックでベクトルの長さを芋぀けたす。



次に、ベクトルの各コンポヌネントをこの倀で陀算したす



今、自問しおください、球ずは䜕ですか 球䜓は、䞎えられた点から等距離にある点の集合です。 球のパラメトリック方皋匏は次のようになりたす



ここで、x0、y0、z0は球の䞭心の座暙、Rはその半埄です。 この䟋では、球䜓の䞭心が原点であり、半埄は単䞀です。 既知の倀を眮き換えお、方皋匏の2぀の郚分のルヌトを取埗したす。 次のこずがわかりたす



文字通り、最埌の倉換は次のこずを瀺しおいたす。「球に属するためには、ベクトルの長さは1に等しくなければなりたせん。」 これが正芏化によっお達成されたものです。

しかし、球䜓の䞭心ず半埄が任意の堎合はどうでしょうか 次の匏を䜿甚しお、それに属するポむントを芋぀けるこずができたす



ここで、pSは球䞊の点、Cは球の䞭心、pNormは以前に正芏化されたベクトル、Rは球の半埄です。 簡単な蚀葉で蚀えば、ここでは次のこずが起こりたす。「球の䞭心から距離Rのグリッド䞊の点に向かっお移動したす」。 結果ずしお、各ベクトルには単䜍長があるため、結果ずしお、すべおの点は球の䞭心からその半埄の距離だけ等距離になり、球の方皋匏が真になりたす。

2.管理


芖点から朜圚的に芋えるセクタヌのグルヌプを取埗する必芁がありたす。 しかし、それを行う方法は ある点を䞭心ずする球䜓があるずしたす。 たた、球䜓にあるセクタヌず、球䜓に近い空間にあるポむントPもありたす。 次に、2぀のベクトルを䜜成したす。1぀は球の䞭心からセクタヌの䞭心に向けられ、もう1぀は球の䞭心から芖点に向けられたす。 図3をご芧ください-これらのベクトル間の角床の絶察倀が90床未満の堎合にのみ、セクタヌを芋るこずができたす。


図3 a-90床未満の角床-セクタヌが朜圚的に芋える。 b-90床より倧きい角床-セクタヌは衚瀺されたせん

この角床を取埗する方法は これを行うには、ベクトルのスカラヌ積を䜿甚したす。 3次元の堎合、次のように蚈算されたす。



スカラヌ積には分垃プロパティがありたす。



先ほど、ベクトルの長さの方皋匏を定矩したした-今では、ベクトルの長さは、このベクトル自䜓のスカラヌ積のルヌトに等しいず蚀えたす。 たたはその逆-ベクトル自䜓のスカラヌ積はその長さの2乗に等しくなりたす。

それでは、コサむンの法則を芋おみたしょう。 2぀の定匏化の1぀は次のようになりたす図4。




図4コサむンの法則

ベクトルの長さをaずbずしおずるず、角床アルファが探しおいたす。 しかし、どのようにしお䟡倀を埗るのでしょうか 芋おくださいbからaを匕くず、aからbに向かうベクトルが埗られたす。ベクトルは方向ず長さだけで特城付けられるので、ベクトルaの終わりにその開始点をグラフィカルに芋぀けるこずができたす。 これに基づいお、cはベクトルb-aの長さに等しいず蚀えたす。 だから、私たちは成功したした



長さの二乗をスカラヌ積ずしお衚珟する



分垃プロパティを䜿甚しお括匧を開きたす



少し切る



そしお最埌に、方皋匏の䞡方の名誉をマむナス2で割るず、



これは、スカラヌ積のもう1぀のプロパティです。 この堎合、ベクトルの長さが1になるようにベクトルを正芏化する必芁がありたす。 角床を蚈算する必芁はありたせん-コサむン倀で十分です。 れロ未満の堎合、このセクタヌは私たちに興味がないず安党に蚀えたす

3.グリッド


プリミティブを描画する方法に぀いお考える時が来たした。 先ほど蚀ったように、セクタヌは私たちのスキヌムの䞻芁なコンポヌネントであるため、朜圚的に芋えるセクタヌごずにグリッドを描き、そのプリミティブがランドスケヌプを圢成したす。 各セルは、2぀の䞉角圢を䜿甚しお衚瀺できたす。 各セルには隣接する面があるため、䞉角圢のほずんどの頂点の倀は2぀以䞊のセルに察しお繰り返されたす。 頂点バッファヌのデヌタを耇補しないように、むンデックスバッファヌを埋めたす。 むンデックスが䜿甚される堎合、グラフィックパむプラむンはそれらの助けを借りお、頂点バッファヌ内のどのプリミティブを凊理するかを決定したす。 図5遞択したトポロゞは、䞉角圢のリストD3D11_PRIMITIVE_TOPOLOGY_TRIANGLELISTです。


図5むンデックスずプリミティブの芖芚衚瀺

セクタヌごずに個別の頂点バッファヌを䜜成するず、費甚がかかりすぎたす。 グリッド空間の座暙で1぀のバッファヌを䜿甚する方がはるかに効率的です。぀たり、xが列で、yが行です。 しかし、それらからどのようにしお球䜓のポむントを埗るのでしょうか セクタヌは、特定のポむントSで始たる正方圢の領域です。すべおのセクタヌの顔の長さは同じです。SLenず呌びたしょう。 グリッドはセクタヌの党領域をカバヌし、行ず列の数も同じです。したがっお、セル面の長さを芋぀けるために、次の方皋匏を構築できたす。



ここで、Lenはセルの面の長さ、MSizeはグリッドの行たたは列の数です。 䞡偎をMSizeで割り、CLenを取埗したす



さらに進みたす。 セクタヌが属する立方䜓の面の空間は、単䜍長さの2぀のベクトルの線圢結合ずしお衚すこずができたす-V1およびV2ず呌びたす。 次の方皋匏がありたす図6を参照。




図6グリッド䞊の点の圢成の芖芚衚瀺

球䞊の点を取埗するために、先に導出した方皋匏を䜿甚したす



4.高さ


この瞬間に達成したこずはすべお、ほずんど颚景に䌌おいたせん。 高さの違い-それを可胜にする䜕かを远加する時が来たした。 原点を䞭心ずする単䜍半埄の球䜓ず、この球䜓にある倚くのポむント{P0、P1、P2 ... PN}があるず想像しおください。 これらの各点は、原点からの単䜍ベクトルずしお衚すこずができたす。 ここで、倀のセットがあり、それぞれが特定のベクトルの長さであるず想像しおください図7。



これらの倀を2次元のテクスチャに保存したす。 ピクセルテクスチャの座暙ず球䞊の点ベクトルの関係を芋぀ける必芁がありたす。 始めたしょう。

デカルト座暙に加えお、球䜓䞊の点は、球座暙系を䜿甚しお蚘述するこずもできたす。 この堎合、座暙は、方䜍角、極角、原点からポむントたでの最短距離の倀の3぀の芁玠で構成されたす。 方䜍角は、X軞ず、原点からXZ平面䞊の点ぞのビヌムの投圱ずの間の角床です。 0〜360床の倀を取るこずができたす。 極角-Y軞ず原点から点たでの光線ずの間の角床。 察空たたは通垞ず呌ばれるこずもありたす。 0〜180床の倀を取りたす。 図8を参照


図8球面座暙

デカルト系から球面に移動するには、次の匏を䜿甚したすY軞が䞊向きであるず仮定したす。



ここで、dはポむントたでの距離、aは極角、bは方䜍角です。 パラメヌタdは、「方皋匏からわかるように」「原点から点たでのベクトルの長さ」ずしお説明するこずもできたす。 正芏化された座暙を䜿甚するず、極角を芋぀けるずきに分割を回避できたす。 実際、なぜこれらの角床が必芁なのでしょうか それぞれを最倧範囲で陀算しお、0から1たでの係数を取埗し、それらを䜿甚しおシェヌダヌのテクスチャから遞択したす。 極角の係数を取埗する堎合、角床が䜍眮する四分の䞀を考慮する必芁がありたす。 「しかし、xがれロの堎合、匏z / xの倀は定矩されたせん」ず蚀いたす。 zがれロに等しい堎合、xの倀に関係なく角床はれロになりたす。

これらの倀に特別なケヌスを远加したしょう。 正芏化された座暙通垞-いく぀かの条件を远加したす法線のX倀がれロでZ倀がれロより倧きい堎合、係数は0.25、XがれロでZがれロより小さい堎合は0.75になりたす。 Zの倀がれロでXがれロより小さい堎合、この堎合、係数は0.5になりたす。 これらはすべお、サヌクルで簡単に確認できたす。 しかし、ZがれロでXがれロより倧きい堎合はどうなりたすかその堎合、0ず1の䞡方が正しいでしょうか 1を遞択したこずを想像しおください。たあ、最小方䜍角0から最倧90床のセクタヌを考えおみたしょう。 次に、このセクタヌが衚瀺するグリッドの最初の行にある最初の3぀の頂点を怜蚎したす。 最初の頂点に぀いおは、条件を満たし、テクスチャ座暙Xを1に蚭定したす。明らかに、この条件は次の2぀の頂点には圓おはたりたせん。 0.1。 ただし、同じ行の最埌の3぀の頂点の角床が270〜360のセクタヌでは、すべおが正しくなりたす。最埌の頂点の条件が機胜し、セット0.9、0.95、1.0が取埗されたす。 結果ずしおれロを遞択するず、セット0.0、0.05、0.1ず0.9、0.95、0.0が埗られたす-いずれの堎合でも、これはかなり顕著な衚面の歪みに぀ながりたす。 それでは、次を適甚したしょう。 セクタヌの䞭心を取り、その䞭心を正芏化しお、球䜓に移動したす。 次に、ベクトル0、0、1によっお正芏化された䞭心のスカラヌ積を蚈算したす。 正匏に蚀えば、このベクトルはXY平面に垂盎であり、セクタヌの䞭心の正芏化されたベクトルずそのスカラヌ積を蚈算したので、䞭心が平面のどちら偎にあるかを理解できたす。 れロより小さい堎合、セクタヌは平面の埌ろにあり、倀1が必芁です。スカラヌ積がれロより倧きい堎合、セクタヌは平面の前にあるため、境界倀は0になりたす図9を参照。


図9テクスチャ座暙に0〜1を遞択する問題

以䞋は、球面からテクスチャ座暙を取埗するためのコヌドです。 泚意しおください-蚈算の゚ラヌのため、れロず等しいかどうかの正垞倀を確認するこずはできたせん。代わりに、それらの絶察倀を特定のしきい倀たずえば、0.001ず比范する必芁がありたす

//norm -   ,       //offset -    ,   norm //zeroTreshold -   (0.001) float2 GetTexCoords(float3 norm, float3 offset) { float tX = 0.0f, tY = 0.0f; bool normXIsZero = abs(norm.x) < zeroTreshold; bool normZIsZero = abs(norm.z) < zeroTreshold; if(normXIsZero || normZIsZero){ if(normXIsZero && norm.z > 0.0f) tX = 0.25f; else if(norm.x < 0.0f && normZIsZero) tX = 0.5f; else if(normXIsZero && norm.z < 0.0f) tX = 0.75f; else if(norm.x > 0.0f && normZIsZero){ if(dot(float3(0.0f, 0.0f, 1.0f), offset) < 0.0f) tX = 1.0f; else tX = 0.0f; } }else{ tX = atan(norm.z / norm.x); if(norm.x < 0.0f && norm.z > 0.0f) tX += 3.141592; else if(norm.x < 0.0f && norm.z < 0.0f) tX += 3.141592; else if(norm.x > 0.0f && norm.z < 0.0f) tX = 3.141592 * 2.0f + tX; tX = tX / (3.141592 * 2.0f); } tY = acos(norm.y) / 3.141592; return float2(tX, tY); } 

頂点シェヌダヌの䞭間バヌゞョンを提䟛したす

 //startPos -    //vec1, vec2 -     //gridStep -   //sideSize -    //GetTexCoords() -      VOut ProcessVertex(VIn input) { float3 planePos = startPos + vec1 * input.netPos.x * gridStep.x + vec2 * input.netPos.y * gridStep.y; float3 sphPos = normalize(planePos); float3 normOffset = normalize(startPos + (vec1 + vec2) * sideSize * 0.5f); float2 tc = GetTexCoords(sphPos, normOffset); float height = mainHeightTex.SampleLevel(mainHeightTexSampler, tc, 0).x; posL = sphPos * (sphereRadius + height); VOut output; output.posH = mul(float4(posL, 1.0f), worldViewProj); output.texCoords = tc; return output; } 

5.照明


颚景の色の照明䟝存性を実珟するために、次の匏を䜿甚したす。



ここで、Iはポむントの色、Ldは光源の色、Kdは照明された衚面の材料の色、aは光源のベクトルず照明された衚面の法線ずの間の角床です。 これは、ランバヌトのコサむンの法則の特別な堎合です。 ここにあるものずその理由を芋おみたしょう。 LdずKdの乗算は、色の成分ごずの乗算、぀たりLd.r * Kd.r、Ld.g * Kd.g、Ld.b * Kd.bを意味したす。 次の状況を想像するず、意味を理解しやすくなる堎合がありたす。オブゞェクトを緑色の光源で照らしたい堎合、オブゞェクトの色は緑色のグラデヌションになるず想定したす。 結果0 * Kd.r、1 * Kd.g、0 * Kd.bは0、Kd.g、0-たさに必芁なものを䞎えたす。 さらに進みたす。 前述のように、正芏化されたベクトル間の角床の䜙匊は、それらのスカラヌ積です。 私たちの芳点からその最倧倀ず最小倀を考えおみたしょう。 ベクトル間の角床の䜙匊が1の堎合、この角床は0です。したがっお、䞡方のベクトルは同䞀盎線䞊にありたす同じ線䞊にありたす。

同じこずがコサむン-1の倀にも圓おはたりたす。この堎合のみ、ベクトルは反察方向を指したす。 法線ベクトルず光​​源ぞのベクトルが共線性状態に近いほど、法線が属する衚面の照明係数が高くなるこずがわかりたす。 たた、その法線が光源ぞの方向に察しお反察方向を向いおいる堎合、衚面を照らすこずはできないず想定されおいたす。そのため、正のコサむン倀のみを䜿甚したす。

私はパラレル゜ヌスを䜿甚しおいるため、その䜍眮は無芖できたす。 考慮すべき唯䞀のこずは、光源にベクトルを䜿甚するこずです。 ぀たり、光線の方向1.0、-1.0、0であれば、ベクトル-1.0、1.0、0を䜿甚する必芁がありたす。 私たちにずっお難しいのは、法線ベクトルだけです。 平面の法線を蚈算するのは簡単です-それを蚘述する2぀のベクトルのベクトル積を生成する必芁がありたす。 ベクトル積は反可換であるこずに泚意するこずが重芁です-因子の順序を考慮する必芁がありたす。 この堎合、次のように、グリッド空間内の頂点の座暙を知っお、䞉角圢の法線を取埗できたすpxずpyの境界ケヌスを考慮しおいないこずに泚意しおください

 float3 p1 = GetPosOnSphere(p); float3 p2 = GetPosOnSphere(float2(px + 1, py)); float3 p3 = GetPosOnSphere(float2(px, py + 1)); float3 v1 = p2 - p1; float3 v2 = p3 - p1; float3 n = normalzie(cross(v1, v2)); 

しかし、それだけではありたせん。 ほずんどのグリッド頂点は、䞀床に4぀の平面に属したす。 蚱容できる結果を埗るには、次のように平均法線を蚈算する必芁がありたす。

 Na = normalize(n0 + n1 + n2 + n3) 

GPUでこのメ゜ッドを実装するのは非垞に高䟡です。法線ずその平均化を蚈算するには2぀のステップが必芁です。 さらに、有効性は䜎いです。 これに基づいお、別の方法を遞択したした-法線マップを䜿甚したす図10。


図10法線マップ

これを䜿甚する原理は、高さマップず同じです-メッシュ頂点の球座暙をテクスチャに倉換し、遞択を行いたす。 このデヌタを盎接䜿甚するこずはできたせん-球䜓を䜿甚しおおり、頂点には独自の法線があり、これを考慮する必芁がありたす。 したがっお、法線マップデヌタを基底のTBN座暙ずしお䜿甚したす。 根拠は䜕ですか 以䞋に䟋を瀺したす。 あなたが宇宙飛行士であり、宇宙のどこかにある灯台に座っおいるず想像しおください。 MCCから「ビヌコンから1メヌトル巊、2メヌトル䞊、3メヌトル前方に移動する必芁がありたす」ずいうメッセヌゞが衚瀺されたす。 これはどのように数孊的に衚珟できたすか 1、0、0* 1 +0、1、0* 2 +0、0、1* 3 =1,2,3。 行列圢匏では、この方皋匏は次のように衚珟できたす。



今、あなたが灯台にも座っおいるず想像しおみおください。MCCからあなたに曞かれたのは、「方向ベクトルを送信したした。最初のベクトルで1メヌトル、2番目で2メヌトル、3番目で3メヌトル進む必芁がありたす」。 新しい座暙の方皋匏は次のようになりたす。



展開された゚ントリは次のようになりたす。



たたはマトリックス圢匏で



したがっお、ベクトルV1、V2、およびV3の行列は基底であり、ベクトル1,2,3はこの基底の空間内の座暙です。

䞀連のベクトル基底Mがあり、ビヌコンに察する盞察的な䜍眮ポむントPがわかっおいるこずを想像しおください。 この基瀎の空間での座暙を知る必芁がありたす-同じ堎所にいるためにこれらのベクトルに沿っおどれだけ前進する必芁があるか。 目的の座暙を想像しおくださいX



P、M、およびXが数倀の堎合、単玔に方皋匏の䞡偎をMで分離したすが、悲しいかな...逆行列のプロパティに埓っお、逆になりたす。



ここで、Iは単䜍行列です。 私たちの堎合、このように芋えたす



これにより䜕が埗られたすか この行列にXを掛けるず、



たた、行列の乗算には結合性の特性があるこずを明確にする必芁がありたす。



ベクトルを3行1列の行列ずしお非垞に正しく考えるこずができたす。

䞊蚘のすべおを考えるず、匏の右偎にXを取埗するために、正しい順序で䞡偎に逆M行列を乗算する必芁があるず結論付けるこずができたす



今埌この結果が必芁になりたす。

問題に戻りたしょう。 正芏盎亀基底を䜿甚したす-これは、V1、V2、およびV3が互いに盎亀90床の角床を圢成し、単䜍長さを持぀こずを意味したす。 接線ベクトルは、V1、V2-二重接線ベクトル、V3-法線ずしお機胜したす。 埓来のDirectX転眮ビュヌでは、マトリックスは次のようになりたす。



ここで、Tは接線ベクトル、Bは䞡接線ベクトル、Nは法線です。 それらを芋぀けたしょう。 通垞、最も簡単な方法は、実際、これらはポむントの正芏化された座暙です。 Bitangentベクトルは、法線ベクトル積ず接線ベクトルに等しくなりたす。 最も難しいのは、接線ベクトルを䜿甚するこずです。 これは、ある点での円の接線の方向に等しくなりたす。 この瞬間を芋おみたしょう。 たず、ある角床aに぀いおXZ平面の単䜍円䞊の点の座暙を芋぀けたす



この点での円の接線の方向は、2぀の方法で芋぀けるこずができたす。 円䞊の点ぞのベクトルず接線ベクトルは盎亀したす-したがっお、関数sinずcosは呚期的であるため、単玔にpi / 2を角床aに远加しお、目的の方向を取埗できたす。 pi / 2のoffsetプロパティによるず



次のベクタヌがありたす。



埮分を䜿甚するこずもできたす-詳现に぀いおは、付録3を参照しおください。したがっお、図11で、各頂点の基底が構築される球䜓を芋るこずができたす。 青いベクトルは法線、赀は接線ベクトル、緑は䞡方向ベクトルです。


図11各頂点にTBNベヌスを持぀球。 èµ€-接線ベクトル、緑-二方向ベクトル、青ベクトル-通垞

基瀎を理解したした-さお、法線マップを取埗したしょう。 これを行うには、Sobelフィルタヌを䜿甚したす。 Sobelフィルタヌは、各ポむント倧たかに蚀えば、茝床倉化ベクトルで画像の茝床募配を蚈算したす。 フィルタヌの原理は、「コア」ず呌ばれる特定の倀のマトリックスを、このマトリックスの次元内の各ピクセルずその隣接ピクセルに適甚する必芁があるずいうこずです。 カヌネルKでピクセルPを凊理するずしたす。画像の境界にない堎合、巊䞊、䞊、右䞊などの8぀の隣接ピクセルがありたす。 それらをtl、t、tb、l、r、bl、b、brず呌びたす。 したがっお、このピクセルにKカヌネルを適甚するこずは次のずおりです。

Pn = tl * K0、0+ t * K0,1+ tb * K0,2+
nbspnbspnbspnbspnbspnbspnbspnbspnbspnbspl * K1、0+ P * K1,1+ r * K1,2+
nbspnbspnbspnbspnbspnbspnbspnbspnbspnbspbl * K2、0+ b * K2,1+ br * K2,2

このプロセスは「畳み蟌み」ず呌ばれたす。 Sobelフィルタヌは2぀のコアを䜿甚しお、垂盎方向ず氎平方向の募配を蚈算したす。 それらをKxおよびKずしお瀺したす。



基瀎がありたす-実装を開始できたす。 たず、ピクセルの明るさを蚈算する必芁がありたす。 PALシステムでは、RGBカラヌモデルからYUVモデルぞの倉換を䜿甚したす。



ただし、画像は元々グレヌスケヌルであったため、この手順は省略できたす。 ここで、KxおよびKyカヌネルを䜿甚しお元のむメヌゞを「瞮小」する必芁がありたす。 したがっお、XおよびYグラデヌションのコンポヌネントを取埗したす。 このベクトルの法線倀も非垞に有甚です-䜿甚したせんが、正芏化された募配法線の倀を含む画像にはいく぀かの有甚な甚途がありたす。 正芏化ずは、次の方皋匏を意味したす



ここで、Vは正芏化する倀、VminずVmaxはこれらの倀の範囲です。 この堎合、最小倀ず最倧倀は生成プロセス䞭に远跡されたす。 Sobelフィルタヌの実装䟋は次のずおりです。

 float SobelFilter::GetGrayscaleData(const Point2 &Coords) { Point2 coords; coords.x = Math::Saturate(Coords.x, RangeI(0, image.size.width - 1)); coords.y = Math::Saturate(Coords.y, RangeI(0, image.size.height - 1)); int32_t offset = (coords.y * image.size.width + coords.x) * image.pixelSize; const uint8_t *pixel = &image.pixels[offset]; return (image.pixelFormat == PXL_FMT_R8) ? pixel[0] : (0.30f * pixel[0] + //R 0.59f * pixel[1] + //G 0.11f * pixel[2]); //B } void SobelFilter::Process() { RangeF dirXVr, dirYVr, magNormVr; for(int32_t y = 0; y < image.size.height; y++) for(int32_t x = 0; x < image.size.width; x++){ float tl = GetGrayscaleData({x - 1, y - 1}); float t = GetGrayscaleData({x , y - 1}); float tr = GetGrayscaleData({x + 1, y - 1}); float l = GetGrayscaleData({x - 1, y }); float r = GetGrayscaleData({x + 1, y }); float bl = GetGrayscaleData({x - 1, y + 1}); float b = GetGrayscaleData({x , y + 1}); float br = GetGrayscaleData({x + 1, y + 1}); float dirX = -1.0f * tl + 0.0f + 1.0f * tr + -2.0f * l + 0.0f + 2.0f * r + -1.0f * bl + 0.0f + 1.0f * br; float dirY = -1.0f * tl + -2.0f * t + -1.0f * tr + 0.0f + 0.0f + 0.0f + 1.0f * bl + 2.0f * b + 1.0f * br; float magNorm = sqrtf(dirX * dirX + dirY * dirY); int32_t ind = y * image.size.width + x; dirXData[ind] = dirX; dirYData[ind] = dirY; magNData[ind] = magNorm; dirXVr.Update(dirX); dirYVr.Update(dirY); magNormVr.Update(magNorm); } if(normaliseDirections){ for(float &dirX : dirXData) dirX = (dirX - dirXVr.minVal) / (dirXVr.maxVal - dirXVr.minVal); for(float &dirY : dirYData) dirY = (dirY - dirYVr.minVal) / (dirYVr.maxVal - dirYVr.minVal); } for(float &magNorm : magNData) magNorm = (magNorm - magNormVr.minVal) / (magNormVr.maxVal - magNormVr.minVal); } 

゜ヌベルフィルタヌには線圢分離性の特性があるため、この方法を最適化できるず蚀わなければなりたせん。

難しい郚分は終わりたした-法線マップのピクセルのRチャンネルずGチャンネルの募配方向のX座暙ずY座暙を曞き留める必芁がありたすZ座暙には1぀䜿甚したす。 たた、3次元の係数ベクトルを䜿甚しおこれらの倀を調敎したす。 以䞋は、コメント付きの法線マップを生成する䟋です。

 //ImageProcessing::ImageData Image -  .        ImageProcessing::SobelFilter sobelFilter; sobelFilter.Init(Image); sobelFilter.NormaliseDirections() = false; sobelFilter.Process(); const auto &resX =sobelFilter.GetFilteredData(ImageProcessing::SobelFilter::SOBEL_DIR_X); const auto &resY =sobelFilter.GetFilteredData(ImageProcessing::SobelFilter::SOBEL_DIR_Y); ImageProcessing::ImageData destImage = {DXGI_FORMAT_R8G8B8A8_UNORM, Image.size}; size_t dstImageSize = Image.size.width * Image.size.height * destImage.pixelSize; std::vector<uint8_t> dstImgPixels(dstImageSize); for(int32_t d = 0 ; d < resX.size(); d++){ //   .     (0.03, 0.03, 1.0) Vector3 norm = Vector3::Normalize({resX[d] * NormalScalling.x, resY[d] * NormalScalling.y, 1.0f * NormalScalling.z}); Point2 coords(d % Image.size.width, d / Image.size.width); int32_t offset = (coords.y * Image.size.width + coords.x) * destImage.pixelSize; uint8_t *pixel = &dstImgPixels[offset]; //    [-1.0, 1.0]  [0.0, 1.0]     [0, 256] pixel[0] = (0.5f + norm.x * 0.5f) * 255.999f; pixel[1] = (0.5f + norm.y * 0.5f) * 255.999f; pixel[2] = (0.5f + norm.z * 0.5f) * 255.999f; } destImage.pixels = &dstImgPixels[0]; SaveImage(destImage, OutFilePath); 

次に、シェヌダヌで法線マップを䜿甚する䟋を瀺したす。

 //texCoords -      ,   .4 //normalL -   //lightDir -     //Ld -    //Kd -     float4 normColor = mainNormalTex.SampleLevel(mainNormalTexSampler, texCoords, 0); //    [0.0, 1.0]  [-1.0, 1.0]    float3 normalT = normalize(2.0f * mainNormColor.rgb - 1.0f); //      [0.0, 1.0]  [0.0, Pi*2.0] float ang = texCoords.x * 3.141592f * 2.0f; float3 tangent; tangent.x = -sin(ang); tangent.y = 0.0f; tangent.z = cos(ang); float3 bitangent = normalize(cross(normalL, tangent)); float3x3 tbn = float3x3(tangent, bitangent, normalL); float3 resNormal = mul(normalT, tbn); float diff = saturate(dot(resNormal, lightDir.xyz)); float4 resColor = Ld * Kd * diff; 

6.詳现レベル


さお、今私たちの颚景がラむトアップされおいたす 月に飛ぶこずができたす-高さマップを点灯し、マテリアルの色を蚭定し、セクタヌをロヌドし、グリッドサむズを{16、16}に蚭定したす...はい、䜕かが足りたせん-{256、256}を入れたす-ああ、䜕かが遅くなりたす、そしおなぜ遠方セクタヌでの詳现床が高いのですか さらに、芳枬者が惑星に近ければ近いほど、芋るこずができるセクタヌは少なくなりたす。 はい...ただやるべきこずがたくさんありたす 最初に、䜙分なセクタヌをカットする方法を芋぀けたしょう。 ここで決定する倀は、惑星の衚面からの芳枬者の高さです-高いほど、より倚くのセクタヌを芋るこずができたす図12


図12芳枬者の身長の凊理枈みセクタヌ数ぞの䟝存性

次のように高さを芋぀けたす-球䜓の䞭心にある芳枬者の䜍眮からベクトルを構築し、その長さを蚈算しお、球䜓の半埄の倀をそこから枛算したす。 前に、オブザヌバヌによるベクトルずセクタヌの䞭心によるベクトルのスカラヌ積がれロ未満である堎合、このセクタヌは関心がありたせん-今ではれロではなく、高さに線圢に䟝存する倀を䜿甚したす。 たず、倉数を決定したしょう。スカラヌ積の最小倀ず最倧倀、および高さの最小倀ず最倧倀を取埗したす。 次の方皋匏系を構築したす



2番目の方皋匏でAを衚珟する



2番目の方皋匏のAを最初の方皋匏に代入したす



最初の方皋匏からBを衚珟する



最初の方皋匏のBを2番目の方皋匏に代入したす



次に、関数の倉数を眮き換えたす



そしお埗る



ここで、HminずHmaxは高さの最小倀ず最倧倀、DminずDmaxはスカラヌ積の最小倀ず最倧倀です。 この問題は別の方法で解決できたす-付録4を参照しおください。

次に、詳现レベルを理解する必芁がありたす。 それぞれがスカラヌ積の範囲を決定したす。 擬䌌コヌドでは、特定のレベルでセクタヌのメンバヌシップを決定するプロセスは次のようになりたす。

                     ,                                    

各レベルの倀の範囲を蚈算する必芁がありたす。 たず、2぀の方皋匏のシステムを構築したす



それを解決しお、私たちは埗る



これらの係数を䜿甚しお、関数を定矩したす



ここで、Rmaxはスカラヌ積のドメむンDH-Dmin、Rminはレベルによっお決定される最小領域です。 0.01の倀を䜿甚したす。 次に、Dmaxから結果を枛算する必芁がありたす



この関数を䜿甚しお、すべおのレベルの゚リアを取埗したす。 以䞋に䟋を瀺したす。

 const float dotArea = dotRange.maxVal - dotRange.minVal; const float Rmax = dotArea, Rmin = 0.01f; float lodsCnt = lods.size(); float A = Rmax; float B = powf(Rmin / Rmax, 1.0f / (lodsCnt - 1.0f)); for(size_t g = 0; g < lods.size(); g++){ lods[g].dotRange.minVal = dotRange.maxVal - A * powf(B, g); lods[g].dotRange.maxVal = dotRange.maxVal - A * powf(B, g + 1); } lods[lods.size() - 1].dotRange.maxVal = 1.0f; 

これで、セクタヌがどの詳现レベルに属するかを刀断できたす図13。


図13詳现レベルに応じたセクタヌの色の区別

次に、グリッドのサむズを把握する必芁がありたす。 レベルごずにグリッドを維持するのは非垞に高䟡です。テッセレヌションを䜿甚しお1぀のグリッドの詳现をオンザフラむで倉曎する方がはるかに効率的です。 これを行うには、通垞の頂点ずピクセルに加えお、ハルシェヌダヌずドメむンシェヌダヌも実装する必芁がありたす。 ハルシェヌダヌの䞻なタスクは、ブレヌクポむントを準備するこずです。 これは、メむン関数ずコントロヌルポむントのパラメヌタヌを蚈算する関数の2぀の郚分で構成されおいたす。 次の属性の倀を必ず指定しおください。
ドメむン
分割
出力トポロゞヌ
出力制埡点
patchconstantfunc
䞉角枬量甚のハルシェヌダヌの䟋を次に瀺したす。

 struct PatchData { float edges[3] : SV_TessFactor; float inside : SV_InsideTessFactor; }; PatchData GetPatchData(InputPatch<VIn, 3> Patch, uint PatchId : SV_PrimitiveID) { PatchData output; flloat tessFactor = 2.0f; output.edges[0] = tessFactor; output.edges[1] = tessFactor; output.edges[2] = tessFactor; output.inside = tessFactor; return output; } [domain("tri")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(3)] [patchconstantfunc("GetPatchData")] VIn ProcessHull(InputPatch<VIn, 3> Patch, uint PointId : SV_OutputControlPointID, uint PatchId : SV_PrimitiveID) { return Patch[PointId]; } 

ご芧のずおり、䞻な䜜業はGetPatchDataで行われたす。 そのタスクは、テッセレヌション係数を確立するこずです。 埌で説明したすが、今床はドメむンシェヌダヌに進みたす。 ハルシェヌダヌから制埡点を受け取り、テッセレヌタヌから座暙を受け取りたす。 䞉角圢の堎合の䜍眮たたはテクスチャ座暙の新しい倀は、次の匏を䜿甚しお蚈算する必芁がありたす

N = C1 * Fx + C2 * Fy + C3 * Fz

ここで、C1、C2、およびC3は制埡点の倀、Fはテッセレヌタの座暙です。 たた、ドメむンシェヌダヌでは、Hullシェヌダヌで指定された倀に察応する倀を持぀ドメむン属性を蚭定する必芁がありたす。 ドメむンシェヌダヌの䟋を次に瀺したす。

 cbuffer buff0 : register(b0) { matrix worldViewProj; } struct PatchData { float edges[3] : SV_TessFactor; float inside : SV_InsideTessFactor; }; [domain("quad")] PIn ProcessDomain(PatchData Patch, float3 Coord : SV_DomainLocation, const OutputPatch<VIn, 3> Tri) { float3 posL = Tri[0].posL * Coord.x + Tri[1].posL * Coord.y + Tri[2].posL * Coord.z; float2 texCoords = Tri[0].texCoords * Coord.x + Tri[1].texCoords * Coord.y + Tri[2].texCoords * Coord.z; PIn output; output.posH = mul(float4(posL, 1.0f), worldViewProj); output.normalW = Tri[0].normalW; output.texCoords = texCoords; return output; } 

この堎合の頂点シェヌダヌの圹割は最小限に抑えられおいたす-私にずっおは、デヌタを次の段階に「投げる」だけです。

次に、同様のものを実装する必芁がありたす。 私たちの䞻なタスクは、テッセレヌション係数を蚈算するこずです。より正確には、オブザヌバの身長ぞの䟝存性を構築したす。 再び方皋匏系を構築したす



以前ず同じ方法で解決するず、



ここで、TminずTmaxはテッセレヌション係数の最小倀ず最倧倀、HminずHmaxは芳枬者の高さの最小倀ず最倧倀です。 私の最小テッセレヌション係数は1です。 最倧倀はレベルごずに個別に蚭定されたす
䟋1、2、4、16。

将来的には、因子の成長を最も近い2のべき乗に制限する必芁がありたす。぀たり、2〜3の倀の堎合、倀を2に蚭定したす。4〜7の倀の堎合、4を蚭定したす。8〜15の倀の堎合、係数は8になりたす。芁因6に぀いおこの問題を解きたしょう。たず、次の方皋匏を解き



たす。察数の



特性に埓っお方皋匏の2぀の郚分の10進数の察数を取り



たす。䞡方の郚分をlog2



しかし、それだけではありたせん。Xは玄2.58です。次に、小数郚分をリセットし、デュヌスを結果の数倀のべき乗する必芁がありたす。詳现レベルのテッセレヌション係数蚈算コヌドは次のずおりです

 float h = camera->GetHeight(); const RangeF &hR = heightRange; for(LodsStorage::Lod &lod : lods){ //derived from system //A + B * Hmax = Lmin //A + B * Hmin = Lmax //and getting A then substitution B in second equality float mTf = (float)lod.GetMaxTessFactor(); float tessFactor = 1.0f + (mTf - 1.0f) * ((h - hR.maxVal) / (hR.minVal - hR.maxVal)); tessFactor = Math::Saturate(tessFactor, RangeF(1.0f, mTf)); float nearPowOfTwo = pow(2.0f, floor(log(tessFactor) / log(2))); lod.SetTessFactor(nearPowOfTwo); } 

7.ノむズ


高さマップのサむズを倉曎せずに、ランドスケヌプのディテヌルを増やす方法を芋おみたしょう。次のこずが思い浮かびたす-高さの倀をグラデヌションノむズテクスチャから取埗した倀に倉曎したす。サンプリングする座暙は、メむン座暙のN倍になりたす。フェッチするずき、アドレス指定のミラヌタむプD3D11_TEXTURE_ADDRESS_MIRRORが䜿甚されたす図14を参照。


図14高さマップのある球+ノむズマップのある球=党高のある球

この堎合、高さは次のように蚈算されたす。

 //float2 tc1 -  ,    ,   //  //texCoordsScale -   .      300 //mainHeightTex, mainHeightTexSampler -    //distHeightTex, distHeightTexSampler -    //maxTerrainHeight -   .    0.03 float2 tc2 = tc1 * texCoordsScale; float4 mainHeighTexColor = mainHeightTex.SampleLevel(mainHeightTexSampler, tc1, 0); float4 distHeighTexColor = distHeightTex.SampleLevel(distHeightTexSampler, tc2, 0); float height = (mainHeighTexColor.x + distHeighTexColor.x) * maxTerrainHeight; 

これたでのずころ、呚期的な性質は倧幅に衚珟されおいたすが、照明ずテクスチャリングの远加により、状況はより良く倉化したす。そしお、グラデヌションノむズテクスチャずは䜕ですか倧たかに蚀っお、これはランダムな倀の栌子です。ラティスのサむズをテクスチャのサむズに合わせる方法を芋おみたしょう。サむズ256 x 256ピクセルのノむズテクスチャを䜜成するずしたす。グリルの寞法がテクスチャのサむズず䞀臎する堎合、それは簡単です-テレビでホワむトノむズのようなものが埗られたす。しかし、グリッドに2 x 2の次元がある堎合はどうでしょうか答えは簡単です-補間を䜿甚したす。線圢補間の定匏化の1぀は次のずおりです。



これは最速ですが、同時に私たちにずっおは最適なオプションではありたせん。コサむンベヌスの補間を䜿甚するこずをお勧めしたす。



ただし、察角線の端セルの巊䞋隅ず右䞊隅に沿っお倀を単玔に補間するこずはできたせん。この堎合、補間を2回適甚する必芁がありたす。栌子のセルの1぀を玹介したしょう。圌女には4぀のコヌナヌがありたす。それらをV1、V2、V3、V4ず呌びたしょう。たた、このセル内には独自の2次元座暙系があり、ポむント0、0はV1に察応し、ポむント1、1はV3に察応したす図15aを参照。座暙0.5、0.5で倀を取埗するには、最初にV1ずV4の間、V2ずV3の間の2぀のX補間倀を取埗し、最埌にこれらの倀の間でYに補間する必芁がありたす図15b。

以䞋に䟋を瀺したす。

 float2 coords(0.5f, 0.5f) float4 P1 = lerp(V1, V4, coords.x); float4 P2 = lerp(V2, V3, coords.x); float4 P = lerp(P1, P2, coords.y) 


図15a-座暙V1、V2、V3、V4の栌子セルの画像。b-セルの䟋での2぀の補間のシヌケンス

、次を実行したしょう-ノむズテクスチャのピクセルごずに、2x4グリッドの補間倀を取埗し、4x4グリッドの補間倀を0.5倍しおから、8x4グリッドの0.25倍などを远加したす。 q䞀定の制限-これはオクタヌブの远加ず呌ばれたす図16。匏は次のずおりです。




図16オクタヌブを远加

する䟋以䞋に実装䟋を瀺したす。

 for(int32_t x = 0; x < size.width; x++) for(int32_t y = 0; y < size.height; y++){ float val = 0.0f; Vector2 normPos = {(float)x / (float)(sideSize - 1), (float)y / (float)(sideSize - 1)}; for(int32_t o = 0; o < octavesCnt; o++){ float frequency = powf(2.0f, (float)(startFrequency + o)); float intencity = powf(intencityFactor, (float)o); Vector2 freqPos = normPos * frequency; Point2 topLeftFreqPos = Cast<Point2>(freqPos); Point2 btmRightFreqPos = topLeftFreqPos + Point2(1, 1); float xFrac = freqPos.x - (float)topLeftFreqPos.x; float yFrac = freqPos.y - (float)topLeftFreqPos.y; float iVal = GetInterpolatedValue(topLeftFreqPos, btmRightFreqPos, xFrac, yFrac); val += iVal * intencity; } noiseValues[y * size.width + x] = val; } 

たた、V1、V2、V3、およびV4の堎合、次のように倀自䜓ずその近傍から合蚈を取埗できたす。

 float GetSmoothValue(const Point2 &Coords) { float corners = (GetValue({Coords.x - 1, Coords.y - 1}) + GetValue({Coords.x + 1, Coords.y - 1}) + GetValue({Coords.x - 1, Coords.y + 1}) + GetValue({Coords.x + 1, Coords.y + 1})) / 16.0f; float sides = (GetValue({Coords.x - 1, Coords.y}) + GetValue({Coords.x + 1, Coords.y}) + GetValue({Coords.x, Coords.y - 1}) + GetValue({Coords.x, Coords.y + 1})) / 8.0f; float center = GetValue(Coords) / 4.0f; return center + sides + corners; } 

これらの倀を補間に䜿甚したす。コヌドの残りは次のずおりです。

 float GetInterpolatedValue(const Point2 &TopLeftCoord, const Point2 &BottomRightCoord, float XFactor, float YFactor) { Point2 tlCoords(TopLeftCoord.x, TopLeftCoord.y); Point2 trCoords(BottomRightCoord.x, TopLeftCoord.y); Point2 brCoords(BottomRightCoord.x, BottomRightCoord.y); Point2 blCoords(TopLeftCoord.x, BottomRightCoord.y); float tl = (useSmoothValues) ? GetSmoothValue(tlCoords) : GetValue(tlCoords); float tr = (useSmoothValues) ? GetSmoothValue(trCoords) : GetValue(trCoords); float br = (useSmoothValues) ? GetSmoothValue(brCoords) : GetValue(brCoords); float bl = (useSmoothValues) ? GetSmoothValue(blCoords) : GetValue(blCoords); float bottomVal = Math::CosInterpolation(bl, br, XFactor); float topVal = Math::CosInterpolation(tl, tr, XFactor); return Math::CosInterpolation(topVal, bottomVal, YFactor); } 

サブセクションの終わりに、ここたで説明しおきたこずはすべお、Perlinのノむズの実装が暙準的な実装ずは少し異なるこずを蚀いたいず思いたす。
高さを蚈算したした。次に、法線の凊理方法を芋おみたしょう。メむンの高さマップの堎合ず同様に、ノむズテクスチャから法線マップを生成する必芁がありたす。次に、シェヌダヌで、メむンカヌドの法線ずノむズテクスチャの法線を远加したす。これは完党に正しいわけではありたせんが、受け入れられる結果をもたらすず蚀わなければなりたせん。以䞋に䟋を瀺したす。
 //float2 texCoords1 -  ,    ,     //mainNormalTex, mainNormalTexSampler -    //distNormalTex, distNormalTexSampler -     float2 texCoords2 = texCoords1 * texCoordsScale; float4 mainNormColor = mainNormalTex.SampleLevel(mainNormalTexSampler, TexCoords1, 0); float4 distNormColor = distNormalTex.SampleLevel(distNormalTexSampler, TexCoords2, 0); float3 mainNormal = 2.0f * mainNormColor.rgb - 1.0f; float3 distNormal = 2.0f * distNormColor.rgb - 1.0f; float3 normal = normalize(mainNormal + distNormal); 

8.ハヌドりェアのむンスタンス化


最適化をしたしょう。擬䌌コヌドでセクタヌを描画するサむクルは次のようになりたす

                      S      V1      V2             

このアプロヌチのパフォヌマンスは非垞に小さいです。いく぀かの最適化オプションがありたす-各セクタヌのスカラヌ積を蚈算しないように、キュヌブの各プレヌンにクアッドツリヌを構築できたす。 V1ずV2の倀は、各セクタヌではなく、それらが属するキュヌブの6぀の平面に察しおも曎新できたす。 3番目のオプション-Instancingを遞択したした。それが䜕であるかに぀いお簡単に説明したす。森を描きたいずしたしょう。ツリヌモデルがあり、ツリヌの䜍眮、可胜なスケヌリングたたは回転-倉換マトリックスのセットもありたす。ワヌルドスペヌスに倉換されたすべおのツリヌの最䞊郚を含む1぀のバッファを䜜成できたす-これは良いオプションです。フォレストはマップ䞊で実行されたせん。しかし、倉換を実装する必芁がある堎合はどうでしょうか-たずえば、颚に揺れる朚。これを行うこずができたす-モデルの頂点のデヌタをN回1぀のバッファヌにコピヌし、ツリヌむンデックス0からNを頂点デヌタに远加したす。次に、倉換行列の配列を曎新し、倉数ずしおシェヌダヌに枡したす。シェヌダヌで、ツリヌむンデックスによっお目的のマトリックスを遞択したす。デヌタの重耇をどのように回避できたすか最初に、これらの頂点がいく぀かのバッファから収集できるずいう事実に泚意を喚起したいず思いたす。頂点を蚘述するには、D3D11_INPUT_ELEMENT_DESC構造䜓のInputSlotフィヌルドに゜ヌスむンデックスを指定する必芁がありたす。これは、モヌフィングフェむシャルアニメヌションを実装するずきに䜿甚できたす。2぀のフェヌス状態を含む2぀の頂点バッファヌがあり、これらの倀を線圢に補間するずしたす。トップを説明する方法は次のずおりです。次に、倉換行列の配列を曎新し、倉数ずしおシェヌダヌに枡したす。シェヌダヌで、ツリヌむンデックスによっお目的のマトリックスを遞択したす。デヌタの重耇をどのように回避できたすか最初に、これらの頂点がいく぀かのバッファから収集できるずいう事実に泚意を喚起したいず思いたす。頂点を蚘述するには、D3D11_INPUT_ELEMENT_DESC構造䜓のInputSlotフィヌルドに゜ヌスむンデックスを指定する必芁がありたす。これは、モヌフィングフェむシャルアニメヌションを実装するずきに䜿甚できたす。2぀のフェヌス状態を含む2぀の頂点バッファヌがあり、これらの倀を線圢に補間するずしたす。トップを説明する方法は次のずおりです。次に、倉換行列の配列を曎新し、倉数ずしおシェヌダヌに枡したす。シェヌダヌで、ツリヌむンデックスによっお目的のマトリックスを遞択したす。デヌタの重耇をどのように回避できたすか最初に、これらの頂点がいく぀かのバッファから収集できるずいう事実に泚意を喚起したいず思いたす。頂点を蚘述するには、D3D11_INPUT_ELEMENT_DESC構造䜓のInputSlotフィヌルドに゜ヌスむンデックスを指定する必芁がありたす。これは、モヌフィングフェむシャルアニメヌションを実装するずきに䜿甚できたす。2぀のフェヌス状態を含む2぀の頂点バッファヌがあり、これらの倀を線圢に補間するずしたす。トップを説明する方法は次のずおりです。頂点を蚘述するには、D3D11_INPUT_ELEMENT_DESC構造䜓のInputSlotフィヌルドに゜ヌスむンデックスを指定する必芁がありたす。これは、モヌフィングフェむシャルアニメヌションを実装するずきに䜿甚できたす。2぀のフェヌス状態を含む2぀の頂点バッファヌがあり、これらの倀を線圢に補間するずしたす。トップを説明する方法は次のずおりです。頂点を蚘述するには、D3D11_INPUT_ELEMENT_DESC構造䜓のInputSlotフィヌルドに゜ヌスむンデックスを指定する必芁がありたす。これは、モヌフィングフェむシャルアニメヌションを実装するずきに䜿甚できたす。2぀のフェヌス状態を含む2぀の頂点バッファヌがあり、これらの倀を線圢に補間するずしたす。トップを説明する方法は次のずおりです。

 D3D11_INPUT_ELEMENT_DESC desc[] = { /*part1*/ {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, /*part2*/ {"POSITION", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 24, D3D11_INPUT_PER_VERTEX_DATA, 0} } 

シェヌダヌでは、頂点を次のように蚘述する必芁がありたす。

 struct VIn { float3 position1 : POSITION0; float3 normal1 : NORMAL0; float2 tex1 : TEXCOORD0; float3 position2 : POSITION1; float3 normal2 : NORMAL1; float2 tex2 : TEXCOORD1; } 

次に、倀を補間するだけです

 float3 res = lerp(input.position1, input.position2, factor); 

なぜこれをしおいるのですか朚の䟋に戻りたしょう。2぀の゜ヌスから頂点を収集したす。最初の゜ヌスにはロヌカル空間の䜍眮、テクスチャ座暙、法線が含たれ、2番目の゜ヌスには4぀の4次元ベクトルの圢匏の倉換マトリックスが含たれたす。頂点の説明は次のようになりたす。

 D3D11_INPUT_ELEMENT_DESC desc[] = { /*part1*/ {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, /*part2*/ {"WORLD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"WORLD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"WORLD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"WORLD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 48, D3D11_INPUT_PER_INSTANCE_DATA, 1}, } 

2番目の郚分では、InputSlotClassフィヌルドはD3D11_INPUT_PER_INSTANCE_DATAであり、InstanceDataStepRateフィヌルドは1であるこずに泚意しおくださいInstanceDataStepRateフィヌルドの簡単な説明に぀いおは、付録1を参照しおください。この堎合、コレクタヌは、タむプD3D11_INPUT_PER_INSTANCE_DATAの゜ヌスからの各芁玠に察しお、タむプD3D11_INPUT_PER_VERTEX_DATAの゜ヌスからのバッファヌ党䜓のデヌタを䜿甚したす。シェヌダヌでは、これらの頂点は次のように蚘述できたす。

 struct VIn { float3 posL : POSITION; float3 normalL : NORMAL; float2 tex : TEXCOORD; row_major float4x4 world : WORLD; }; 

属性D3D11_USAGE_DYNAMICおよびD3D11_CPU_ACCESS_WRITEで2番目のバッファヌを䜜成するこずにより、CPU偎から曎新できたす。この皮のゞオメトリを描画するには、DrawInstancedたたはDrawIndexedInstancedを呌び出す必芁がありたす。DrawInstancedIndirectおよびDrawIndexedInstancedIndirectぞの呌び出しもありたす-それらに぀いおは、付録2を参照しおください。

バッファの蚭定ずDrawIndexedInstanced関数の䜿甚䟋は次のずおりです。

 //vb -   //tb - ""  //ib -   //vertexSize -      //instanceSize -    ""  //indicesCnt -   //instancesCnt -  "" std::vector<ID3D11Buffer*> buffers = {vb, tb}; std::vector<UINT> strides = {vertexSize, instanceSize}; std::vector<UINT> offsets = {0, 0}; deviceContext->IASetVertexBuffers(0,buffers.size(),&buffers[0],&strides[0],&offsets[0]); deviceContext->IASetIndexBuffer(ib, DXGI_FORMAT_R32_UINT, 0); deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); deviceContext->DrawIndexedInstanced(indicesCnt, instancesCnt, 0, 0, 0); 

最埌に、トピックに戻りたしょう。セクタヌが属する平面䞊の点ず、この平面を蚘述する2぀のベクトルを䜿甚しお、セクタヌを手動で蚘述したす。したがっお、ピヌクは2぀の゜ヌスで構成されたす。1぀目はグリッド空間の座暙、2぀目はセクタヌデヌタです。頂点の説明は次のようになりたす。

 std::vector<D3D11_INPUT_ELEMENT_DESC> meta = { //    {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0} //   {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1}, //   {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 12, D3D11_INPUT_PER_INSTANCE_DATA, 1}, //  {"TEXCOORD", 2, DXGI_FORMAT_R32G32B32_FLOAT, 1, 24, D3D11_INPUT_PER_INSTANCE_DATA, 1} } 

座暙をグリッド空間に栌玍するために、3次元ベクトルを䜿甚しおいるこずに泚意しおくださいz座暙は䜿甚されたせん

9.錐台カリング


もう1぀の重芁な最適化コンポヌネントは、Frustumカリングです。可芖性のピラミッドは、カメラが「芋る」シヌンの領域です。構築方法 たず、ポむントは4぀の座暙系ロヌカル、ワヌルド、ビュヌ、投圱の各座暙系にある可胜性があるこずに泚意しおください。それらの間の遷移は、マトリックス、䞖界、皮、および投圱マトリックスによっお実行され、倉換は、ロヌカルから䞖界、䞖界から皮、そしお最埌に皮から投圱空間に順番に行われなければなりたせん。これらの行列を乗算するこずにより、これらのすべおの倉換を1぀にたずめるこずができたす。

透芖投圱法を䜿甚したす。これは、いわゆる「均䞀陀算」を意味したす。ベクトルPx、Py、Pz、1に投圱行列を乗算した埌、その成分をこのベクトルのW成分に分割したす。投圱空間ぞの移行ず均䞀な分割の埌、ポむントはNDC空間に衚瀺されたす。NDC空間は3぀の座暙x、y、zのセットです。xずyは[-1、1]に属し、zは[0,1]に属したすOpenGLではパラメヌタヌが倚少異なるず蚀わなければなりたせん。

それでは、問題の解決に取りかかりたしょう。私の堎合、ピラミッドはビュヌポヌトにありたす。それを蚘述する6぀の平面が必芁です図17a。平面は、法線ずこの平面に属する点を䜿甚しお蚘述できたす。たず、ポむントを取埗したしょう-これのために、NDC空間で次の座暙セットを取埗したす。

 std::vector<Point4F> pointsN = { {-1.0f, -1.0f, 0.0f, 1.0f}, {-1.0f, 1.0f, 0.0f, 1.0f}, { 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, -1.0f, 0.0f, 1.0f}, {-1.0f, -1.0f, 1.0f, 1.0f}, {-1.0f, 1.0f, 1.0f, 1.0f}, { 1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, -1.0f, 1.0f, 1.0f} }; 

最初の4点でzの倀は0です-これは、それらが近いクリッピング平面に属し、最埌の4点でzが1-それらが遠いクリッピング平面に属するこずを意味したす。次に、これらのポむントをビュヌポヌトに倉換する必芁がありたす。しかし、どのように

宇宙飛行士に぀いおの䟋を芚えおおいおください-そしお、これは同じこずです。点に逆投圱行列を掛ける必芁がありたす。確かに、この埌、それぞれを座暙Wで割る必芁がありたす。その結果、必芁な座暙を取埗したす図17b。ここで、法線を扱いたしょう-それらはピラミッド内に向けられるべきなので、ベクトル積の蚈算に必芁な順序を遞択する必芁がありたす。

 Matrix4x4 invProj = Matrix4x4::Inverse(camera->GetProjMatrix()); std::vector<Point3F> pointsV; for(const Point4F &pN : pointsN){ Point4F pV = invProj.Transform(pN); pV /= pV.w; pointsV.push_back(Cast<Point3F>(pV)); } planes[0] = {pointsV[0], pointsV[1], pointsV[2]}; //near plane planes[1] = {pointsV[4], pointsV[5], pointsV[6]}; //far plane planes[2] = {pointsV[0], pointsV[1], pointsV[4]}; //left plane planes[3] = {pointsV[2], pointsV[3], pointsV[6]}; //right plane planes[4] = {pointsV[1], pointsV[2], pointsV[6]}; //top plane planes[5] = {pointsV[0], pointsV[3], pointsV[7]}; //bottom plane planes[0].normal *= -1.0f; planes[5].normal *= -1.0f; 


図17可芖性の

ピラミッドピラミッドは構築されおいたす-䜿甚する時が来たした。ピラミッド内に収たらないセクタヌは描画したせん。セクタヌが可芖性のピラミッド内にあるかどうかを刀断するために、このセクタヌの䞭心にある境界球を確認したす。これでは正確な結果は埗られたせんが、この堎合、いく぀かの䜙分なセクタヌが描画されるずいう事実に問題はありたせん。球の半埄は次のように蚈算されたす。



ここで、TRはセクタヌの右䞊隅、BLは巊䞋隅です。すべおのセクタヌは同じ面積を持っおいるため、半埄を䞀床蚈算すれば十分です。

セクタヌを蚘述する球䜓が可芖性ピラミッド内にあるかどうかをどのように刀断したすかたず、球䜓が平面ず亀差するかどうかを刀断する必芁がありたす。そうでない堎合は、球䜓のどちら偎からであるかを刀断したす。球の䞭心にあるベクトルを取埗したしょう



ここで、Pは平面䞊の点、Sは球の䞭心です。次に、法線平面でこのベクトルのスカラヌ積を蚈算したす。方向は、スカラヌ積の笊号を䜿甚しお決定できたす。前述のように、正の堎合、球は平面の前にあり、負の堎合、球は埌ろにありたす。球が平面ず亀差するかどうかを刀断するために残っおいたす。 N法線ベクトルずVの2぀のベクトルを取りたしょう。NからVぞのベクトルを䜜成したしょう。Kず呌びたしょう。したがっお、Kず正匏に90床の角床を圢成するような長さNを芋぀ける必芁がありたす。 Kは盎亀しおいたした。さおドック、図18aを芋おください-盎角䞉角圢のプロパティから



、コサむンを芋぀ける必芁があるこずがわかりたす。前述のスカラヌ積プロパティを䜿甚しお、



䞡偎を| V | * | N |で陀算したす。そしお埗る



この結果を䜿甚したす



から| V | V | |それは䞊のカットバックするこずが可胜で、ちょうど数だし、我々が埗る



ベクトルNが正芏化されおいるので、結果の倀によっお、我々は単に乗算それ最埌のステップを、それ以倖のベクトルが正芏化されなければならない-この堎合には、このような最終方皋匏ルックス



どこでDこれが新しいベクタヌです。このプロセスは「ベクトル投圱」ず呌ばれたす図18b。しかし、なぜこれが必芁なのでしょうか我々は、ベクトルの長さず方向によっお決定され、その䜍眮から倉化しないこずを知っおいる-我々はそれがSを指摘するようにDを配眮する堎合、その長さは、平面ris.18sぞSから最小距離に等しいこずは、この手段を


図.18 a NのVぞの投圱、bポむントに適甚される投圱Nの長さの芖芚的衚珟、s芖芚的衚珟
Sを䞭心ずする球に適甚される投圱Nの長さ


投圱ベクトルは必芁ないので、長さを蚈算するだけで十分です。 Nが単䜍ベクトルである堎合、Vのスカラヌ積をNだけで蚈算する必芁がありたす。すべおをたずめるず、球の䞭心ず平面の法線ずのベクトルのスカラヌ積の倀がれロより倧きく半埄より小さい堎合、球は最終的に平面ず亀差するず結論付けるこずができたすこの球。

球䜓が可芖性のピラミッドの内偎にあるず䞻匵するためには、それが平面の1぀ず亀差するか、各平面の前にあるこずを確認する必芁がありたす。友だちに質問するこずができたす-球䜓が亀差せず、少なくずも1぀の飛行機の背埌にある堎合、それは間違いなく可芖性のピラミッドの倖偎にありたす。だからやる。球䜓の䞭心を、ピラミッドが配眮されおいるのず同じ空間、぀たりビュヌの空間に倉換するこずに泚意しおください。

 bool Frustum::TestSphere(const Point3F &Pos, float Radius, const Matrix4x4 &WorldViewMatrix) const { Point3F posV = WorldViewMatrix.Transform(Pos); for(const Plane &pl : planes){ Vector3 toSphPos = posV - pl.pos; if(Vector3::Dot(toSphPos, pl.normal) < -Radius) return false; } return true; } 

10.クラック


解決しなければならないもう1぀の問題は、詳现レベルの境界での亀裂です図19。


図19景芳亀裂のデモンストレヌション

たず、詳现レベルの境界にあるセクタヌを特定する必芁がありたす。䞀芋、これはリ゜ヌス集玄型のタスクのようです-各レベルのセクタヌの数は絶えず倉化しおいるためです。ただし、隣接デヌタを䜿甚する堎合、゜リュヌションは倧幅に簡玠化されたす。隣接デヌタずは䜕ですか各セクタヌには4぀の近隣がありたす。それらぞの参照のセット-ポむンタヌたたはむンデックス-隣接デヌタです。圌らの助けを借りお、どのセクタヌが囜境にあるかを簡単に刀断できたす-隣人がどのレベルに属しおいるかを確認するだけです。

さお、各セクタヌの隣人を芋぀けたしょう。繰り返したすが、すべおのセクタヌをルヌプする必芁はありたせん。グリッド空間のX座暙ずY座暙を持぀セクタヌで䜜業しおいるず想像しおください。

立方䜓の端に觊れない堎合、その隣の座暙は次のようになりたす

䞊からの隣人-X、Y-1
䞋からの隣人-X、Y + 1
巊からの隣人-X-1、Y
右からの隣人-X + 1、Y

セクタヌがrib骚に觊れる堎合、その特別な容噚を入れたす。6぀の面すべおを凊理した埌、キュヌブのすべおの境界セクタヌが含たれたす。このコンテナでは、敎理する必芁がありたす。事前に、各セクタヌの゚ッゞを蚈算したす。

 struct SectorEdges { CubeSectors::Sector *owner; typedef std::pair<Point3F, Point3F> Edge; Edge edges[4]; }; std::vector<SectorEdges> sectorsEdges; //borderSectors -     for(CubeSectors::Sector &sec : borderSectors){ //     ,    , //    Vector3 v1 = sec.vec1 * sec.sideSize; Vector3 v2 = sec.vec2 * sec.sideSize; //sec.startPos -      SectorEdges secEdges; secEdges.owner = &sec; secEdges.edges[ADJ_BOTTOM] = {sec.startPos, sec.startPos + v1}; secEdges.edges[ADJ_LEFT] = {sec.startPos, sec.startPos + v2}; secEdges.edges[ADJ_TOP] = {sec.startPos + v2, sec.startPos + v2 + v1}; secEdges.edges[ADJ_RIGHT] = {sec.startPos + v1, sec.startPos + v2 + v1}; sectorsEdges.push_back(secEdges); } 

次はバストそのものです

 for(SectorEdges &edgs : sectorsEdges) for(size_t e = 0; e < 4; e++) if(edgs.owner->adjacency[e] == nullptr) FindSectorEdgeAdjacency(edgs, (AdjacencySide)e, sectorsEdges); 

FindSectorEdgeAdjacency関数は次のようになりたす

 void CubeSectors::FindSectorEdgeAdjacency(SectorEdges &Sector, CubeSectors::AdjacencySide Side, std::vector<SectorEdges> &Neibs) { SectorEdges::Edge &e = Sector.edges[Side]; for(SectorEdges &edgs2 : Neibs){ if(edgs2.owner == Sector.owner) continue; for(size_t e = 0; e < 4; e++){ SectorEdges::Edge &e2 = edgs2.edges[e]; if((Math::Equals(e.first, e2.first) && Math::Equals(e.second, e2.second)) || (Math::Equals(e.second, e2.first) && Math::Equals(e.first, e2.second))) { Sector.owner->adjacency[Side] = edgs2.owner; edgs2.owner->adjacency[e] = Sector.owner; return; } } } } 

2぀のセクタヌの隣接デヌタを曎新するこずに泚意しおください-目的のセクタヌず芋぀かった隣接。

次に、隣接デヌタを䜿甚しお、詳现レベルの境界に属するセクタヌの゚ッゞを芋぀ける必芁がありたす。そのような蚈画-レンダリングする前に、境界セクタヌを芋぀けたす。次に、各セクタヌのむンスタンスバッファヌに、䞻な
情報に加えお、隣接セクタヌのテッセレヌション係数ずテッセレヌション係数の4次元ベクトルを曞き蟌みたす。頂点の説明は次のようになりたす。

 std::vector<D3D11_INPUT_ELEMENT_DESC> meta = { //    {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0} //   {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0,D3D11_INPUT_PER_INSTANCE_DATA, 1}, //   {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1, 12, D3D11_INPUT_PER_INSTANCE_DATA, 1}, //  {"TEXCOORD", 2, DXGI_FORMAT_R32G32B32_FLOAT, 1, 24, D3D11_INPUT_PER_INSTANCE_DATA, 1}, //    {"TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 36, D3D11_INPUT_PER_INSTANCE_DATA, 1}, //   {"TEXCOORD", 4, DXGI_FORMAT_R32_FLOAT, 1, 52, D3D11_INPUT_PER_INSTANCE_DATA, 1} } 

詳现レベルでセクタヌを割り圓おた埌、各セクタヌの隣接テッセレヌション係数を決定したす。

 for(LodsStorage::Lod &lod : lods){ const std::vector<Sector*> &sectors = lod.GetSectors(); bool lastLod = lod.GetInd() == lods.GetCount() - 1; for(Sector *s : sectors){ int32_t tessFacor = s->GetTessFactor(); s->GetBorderTessFactor() = { GetNeibTessFactor(s, Sector::ADJ_BOTTOM, tessFacor, lastLod), GetNeibTessFactor(s, Sector::ADJ_LEFT, tessFacor, lastLod), GetNeibTessFactor(s, Sector::ADJ_TOP, tessFacor, lastLod), GetNeibTessFactor(s, Sector::ADJ_RIGHT, tessFacor, lastLod) }; } } 

隣接するテッセレヌション係数を怜玢する関数

 float Terrain::GetNeibTessFactor(Sector *Sec, Sector::AdjacencySide Side, int32_t TessFactor, bool IsLastLod) { Sector *neib = Sec->GetAdjacency()[Side]; int32_t neibTessFactor = neib->GetTessFactor(); return (neibTessFactor < TessFactor) ? (float)neibTessFactor : 0.0f; } 

れロを返す堎合、サむド偎の隣人は私たちにずっお関心がありたせん。先に進み、テッセレヌション係数が倧きいレベルの偎面から亀裂を陀去する必芁があるず蚀いたす。

シェヌダヌに枡したしょう。たず、テッセレヌタの座暙を䜿甚しおグリッド座暙を取埗する必芁があるこずを思い出させおください。次に、これらの座暙が立方䜓の端の点に倉換され、この点が正芏化されたす-これで、球䞊の点ができたした。

 float3 p = Tri[0].netPos * Coord.x + Tri[1].netPos * Coord.y + Tri[2].netPos * Coord.z; float3 planePos = Tri[0].startPos + Tri[0].vec1 * px * gridStep.x + Tri[0].vec2 * py * gridStep.y; float3 sphPos = normalize(planePos); 

たず、頂点がグリッドの最初たたは最埌の行に属しおいるか、最初たたは最埌の列に属しおいるかを調べる必芁がありたす。この堎合、頂点はセクタヌの端に属したす。しかし、これでは十分ではありたせん。頂点が詳现レベルの境界に属しおいるかどうかを刀断する必芁がありたす。これを行うために、隣接セクタヌに関する情報、たたはむしろそれらのテセレヌションレベルを䜿甚したす。

 float4 bTf = Tri[0].borderTessFactor; bool isEdge = (bTf.x != 0.0f && py == 0.0f) || //bottom (bTf.y != 0.0f && px == 0.0f) || //left (bTf.z != 0.0f && py == gridSize.y) || //top (bTf.w != 0.0f && px == gridSize.x) //right 

珟圚、䞻な段階は実際には亀裂の陀去です。図20をご芧ください。赀い線は、2番目の詳现レベルに属するピヌクの゚ッゞです。 2本の青い線-3番目の詳现レベルの境界。 V3は赀い線に属しおいる必芁がありたす-぀たり、第2レベルの危機にonしおいたす。高さV1ずV2は䞡方のレベルで等しいため、V3はそれらの間の線圢補間によっお芋぀けるこずができたす




図20線の圢で亀裂を圢成する面のデモンストレヌション

これたでのずころ、V1ずV2も係数Fもありたせん。たず、ポむントV3のむンデックスを芋぀ける必芁がありたす。぀たり、グリッドのサむズが32 x 32で、テッセレヌション係数が4の堎合、このむンデックスは0〜12832 * 4になりたす。グリッド空間pにはすでに座暙がありたす-この䟋のフレヌムワヌクでは、たずえば15.5、16になりたす。むンデックスを取埗するには、座暙pの1぀にテッセレヌション係数を乗算したす。たた、顔の始たりずその終わりぞの方向、぀たりセクタヌのコヌナヌの1぀も必芁です。

 float edgeVertInd = 0.0f; float3 edgeVec = float3(0.0f, 0.0f, 0.0f); float3 startPos = float3(0.0f, 0.0f, 0.0f); uint neibTessFactor = 0; if(bTf.x != 0.0f && py == 0.0f){ // bottom edgeVertInd = px * Tri[0].tessFactor; edgeVec = Tri[0].vec1; startPos = Tri[0].startPos; neibTessFactor = (uint)Tri[0].borderTessFactor.x; }else if(bTf.y != 0.0f && px == 0.0f){ // left edgeVertInd = py * Tri[0].tessFactor; edgeVec = Tri[0].vec2; startPos = Tri[0].startPos; neibTessFactor = (uint)Tri[0].borderTessFactor.y; }else if(bTf.z != 0.0f && py == gridSize.y){ // top edgeVertInd = px * Tri[0].tessFactor; edgeVec = Tri[0].vec1; startPos = Tri[0].startPos + Tri[0].vec2 * (gridStep.x * gridSize.x); neibTessFactor = (uint)Tri[0].borderTessFactor.z; }else if(bTf.w != 0.0f && px == gridSize.x){ // right edgeVertInd = py * Tri[0].tessFactor; edgeVec = Tri[0].vec2; startPos = Tri[0].startPos + Tri[0].vec1 * (gridStep.x * gridSize.x); neibTessFactor = (uint)Tri[0].borderTessFactor.w; } 

次に、V1ずV2のむンデックスを芋぀ける必芁がありたす。数字の3があるずしたす。2の倍数である2぀の最も近い数字を芋぀ける必芁がありたす。これを行うには、3を2で割った䜙りを蚈算したす1に等しい。次に、この剰䜙を3に枛算たたは加算しお、目的の結果を取埗したす。たた、むンデックスでは、2぀ではなく、詳现レベルのテッセレヌション係数の比率がありたす。぀たり、3番目のレベルの係数が16で、2番目のレベル2の係数が8の堎合、比率は8になりたす。ここで、高さを取埗するには、たず球の察応するポむントを取埗し、゚ッゞのポむントを正芏化する必芁がありたす。リブの始点ず方向はすでに準備されおいたす。V1からV2たでのベクトルの長さを蚈算するこずは残っおいたす。元のメッシュセルの゚ッゞの長さはgridStep.xであるため、必芁な長さはgridStep.x / Tri [0] .tessFactorです。次に、前述のように、球䜓のポむントで高さを取埗したす。

 float GetNeibHeight(float3 EdgeStartPos, float3 EdgeVec, float VecLen, float3 NormOffset) { float3 neibPos = EdgeStartPos + EdgeVec * VecLen; neibPos = normalize(neibPos); return GetHeight(neibPos, NormOffset); } float vertOffset = gridStep.x / Tri[0].tessFactor; uint tessRatio = (uint)tessFactor / (uint)neibTessFactor; uint ind = (uint)edgeVertInd % tessRatio; uint leftNeibInd = (uint)edgeVertInd - ind; float leftNeibHeight = GetNeibHeight(startPos, edgeVec, vertOffset * leftNeibInd, normOffset); uint rightNeibInd = (uint)edgeVertInd + ind; float rightNeibHeight = GetNeibHeight(startPos, edgeVec, vertOffset * rightNeibInd, normOffset); 

さお、最新のコンポヌネントは係数Fです。陀算の残りを係数の比indで陀算し、係数の比tessRatioで陀算するこずで埗られたす。

 float factor = (float)ind / (float)tessRatio; 

最終段階-高さの線圢補間ず新しい頂点の取埗

 float avgHeight = lerp(leftNeibHeight, rightNeibHeight, factor); posL = sphPos * (sphereRadius + avgHeight); 

セクタヌの境界が1たたは0に等しい゚ッゞのテクスチャ座暙にある堎所にも亀裂が発生する堎合がありたす。この堎合、2぀の座暙の高さの平均倀を取埗したす。

 float GetHeight(float2 TexCoords) { float2 texCoords2 = TexCoords * texCoordsScale; float mHeight = mainHeightTex.SampleLevel(mainHeightTexSampler, TexCoords, 0).x; float dHeight = distHeightTex.SampleLevel(distHeightTexSampler, texCoords2, 0).x; return (mHeight + dHeight) * maxTerrainHeight; } float GetHeight(float3 SphPos, float3 NormOffset) { float2 texCoords1 = GetTexCoords(SphPos, NormOffset); float height = GetHeight(texCoords1); if(texCoords1.x == 1.0f){ float height2 = GetHeight(float2(0.0f, texCoords1.y)); return lerp(height, height2, 0.5f); }else if(texCoords1.x == 0.0f){ float height2 = GetHeight(float2(1.0f, texCoords1.y)); return lerp(height, height2, 0.5f); }else return height; } 

11. GPUでの凊理


セクタヌの凊理をGPUに転送したしょう。 2぀のコンピュヌトシェヌダヌがありたす。1぀目は可芖性ピラミッドを切り取り、詳现レベルを決定し、2぀目は境界テッセレヌション係数を受け取っおクラックを陀去したす。 CPUの堎合のように、切断するたでセクタヌの隣接を正しく刀断できないため、2段階に分ける必芁がありたす。䞡方のシェヌダヌは詳现レベルのデヌタを䜿甚し、セクタヌを凊理するため、セクタヌずロッドの2぀の䞀般的な構造を導入したした。

 struct Sector { float3 vec1, vec2; float3 startPos; float3 normCenter; int adjacency[4]; float borderTessFactor[4]; int lod; }; struct Lod { RangeF dotRange; float tessFactor; float padding; float4 color; }; 

入力セクタヌに関する初期情報を含む、䞭間最初の段階の結果ずしお取埗されたセクタヌのデヌタを含む、最終レンダリングに転送されたすの3぀のメむンバッファヌを䜿甚したす。入力バッファヌのデヌタは倉曎されないため、D3D11_BUFFER_DESC構造䜓のUsageフィヌルドに倀D3D11_USAGE_IMMUTABLEを䜿甚するのが合理的です。すべおのセクタヌのデヌタを曞き蟌むだけで、隣接デヌタにはセクタヌポむンタヌではなくセクタヌむンデックスを䜿甚したす。詳现レベルのむンデックスずテッセレヌションの境界係数には、れロ倀を蚭定したす。

 static const size_t sectorSize = sizeof(Vector3) + //vec1 sizeof(Vector3) + //vec2 sizeof(Point3F) + //normCenter sizeof(Point3F) + //startPos sizeof(Point4) + //adjacency sizeof(Vector4) + //borderTessFactor sizeof(int32_t);//lod size_t sectorsDataSize = sectors.GetSectors().size() * sectorSize; std::vector<char> sectorsData(sectorsDataSize); char* ptr = &sectorsData[0]; const Sector* firstPtr = &sectors.GetSectors()[0]; for(const Sector &sec : sectors){ Utils::AddToStream<Vector3>(ptr, sec.GetVec1()); Utils::AddToStream<Vector3>(ptr, sec.GetVec2()); Utils::AddToStream<Point3F>(ptr, sec.GetStartPos()); Utils::AddToStream<Point3F>(ptr, sec.GetNormCenter()); Utils::AddToStream<int32_t>(ptr, sec.GetAdjacency()[0] - firstPtr); Utils::AddToStream<int32_t>(ptr, sec.GetAdjacency()[1] - firstPtr); Utils::AddToStream<int32_t>(ptr, sec.GetAdjacency()[2] - firstPtr); Utils::AddToStream<int32_t>(ptr, sec.GetAdjacency()[3] - firstPtr); Utils::AddToStream<Vector4>(ptr, Vector4()); Utils::AddToStream<int32_t>(ptr, 0); } inputData = Utils::DirectX::CreateBuffer(&sectorsData[0],//Raw data sectorsDataSize,//Buffer size D3D11_BIND_SHADER_RESOURCE,//bind flags D3D11_USAGE_IMMUTABLE,//usage 0,//CPU access flags D3D11_RESOURCE_MISC_BUFFER_STRUCTURED,//misc flags sectorSize);//structure byte stride 

次に、䞭間バッファに぀いお少し説明したす。最初のシェヌダヌの出力ず2番目のシェヌダヌの入力ずいう2぀の圹割を果たしたす。したがっお、BindFlagsフィヌルドをD3D11_BIND_UNORDERED_ACCESSに蚭定したす。D3D11_BIND_SHADER_RESOURCE。たた、シェヌダヌが䜜業結果を曞き蟌むこずができるUnorderedAccessViewず、バッファヌを入力ずしお䜿甚するShaderResourceViewの2぀のディスプレむを䜜成したす。そのサむズは、以前に䜜成された入力バッファず同じになりたす

 UINT miscFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; intermediateData = Utils::DirectX::CreateBuffer( sectors.GetSectors().size() * sectorSize,//Buffer size miscFlags, D3D11_USAGE_DEFAULT,//usage 0,//CPU access flags D3D11_RESOURCE_MISC_BUFFER_STRUCTURED,//misc flags sectorSize);//structure byte stride intermediateUAW = Utils::DirectX::CreateUnorderedAccessView( intermediateData, D3D11_BUFFER_UAV{0, sectors.GetSectors().size(), 0}); intermediateSRV = Utils::DirectX::CreateShaderResourceView( intermediateData, D3D11_BUFFEREX_SRV{0, sectors.GetSectors().size(), 0}); 

次に、最初のシェヌダヌの䞻な機胜を瀺したす。

 StructuredBuffer<Sector> inputData : register(t0); RWStructuredBuffer<Sector> outputData : register(u0); [numthreads(1, 1, 1)] void Process( int3 TId : SV_DispatchThreadID ) { int ind = TId.x; Sector sector = inputData[ind]; float dotVal = dot(toWorldPos, sector.normCenter); if(dotVal < dotRange.minVal || dotVal > dotRange.maxVal){ outputData[ind] = sector; return; } if(!IsVisible(sector.normCenter)){ outputData[ind] = sector; return; } for(int l = 0; l < 4; l++){ Lod lod = lods[l]; if(dotVal >= lod.dotRange.minVal && dotVal <= lod.dotRange.maxVal) sector.lod = l + 1; } outputData[ind] = sector; } 

スカラヌ積を蚈算した埌、セクタヌが朜圚的に芋える領域にあるかどうかを確認したす。次に、前に瀺したFrustum :: TestSphere呌び出しず同䞀のIsVisible呌び出しを䜿甚しお、その可芖性の事実を明確にしたす。関数の操䜜は、倉数worldView、sphereRadius、frustumPlanesPosV、およびfrustumPlanesNormalsVに䟝存したす。これらの倀は、事前にシェヌダヌに枡す必芁がありたす。次に、詳现レベルを決定したす。単䞀からのレベルむンデックスを瀺しおいるこずに泚意しおください。これは、第2段階で詳现レベルがれロのセクタヌを砎棄するために必芁です。

次に、第2段階のバッファヌを準備する必芁がありたす。 Computeシェヌダヌの出力ずテッセレヌタヌの入力ずしおバッファヌを䜿甚したす-このため、BindFlagsフィヌルドで倀D3D11_BIND_UNORDERED_ACCESSを指定する必芁がありたす| D3D11_BIND_VERTEX_BUFFER。バッファデヌタを盎接操䜜する必芁があるため、MiscFlagsフィヌルドに倀D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWSを指定したす。このようなバッファを衚瀺するには、FlagsフィヌルドにDXGI_FORMAT_R32_TYPELESS倀を䜿甚し、NumElementsフィヌルドに4぀のバッファすべおを瀺したす

 size_t instancesByteSize = instanceByteSize * sectors.GetSectors().size(); outputData = Utils::DirectX::CreateBuffer(instancesByteSize, D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DEFAULT, 0, D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS, 0); D3D11_BUFFER_UAV uavParams = {0, instancesByteSize / 4, D3D11_BUFFER_UAV_FLAG_RAW}; outputUAW = Utils::DirectX::CreateUnorderedAccessView(outputData, uavParams, DXGI_FORMAT_R32_TYPELESS); 

カりンタヌも必芁になりたす。これを䜿甚しお、シェヌダヌのメモリをアドレス指定し、DrawIndexedInstanced呌び出しのinstanceCount匕数でその最終倀を䜿甚したす。カりンタヌをサむズが16バむトのバッファヌずしお実装したした。たた、D3D11_BUFFER_UAVフィヌルドのFlagsフィヌルドに衚瀺を䜜成するずきに、倀D3D11_BUFFER_UAV_FLAG_COUNTERを䜿甚したした

 counter = Utils::DirectX::CreateBuffer(sizeof(UINT), D3D11_BIND_UNORDERED_ACCESS, D3D11_USAGE_DEFAULT, 0, D3D11_RESOURCE_MISC_BUFFER_STRUCTURED, 4); D3D11_BUFFER_UAV uavParams = {0, 1, D3D11_BUFFER_UAV_FLAG_COUNTER}; counterUAW = Utils::DirectX::CreateUnorderedAccessView(counter, uavParams); 

2番目のシェヌダヌコヌドを䜿甚したす

 StructuredBuffer<Sector> inputData : register(t0); RWByteAddressBuffer outputData : register(u0); RWStructuredBuffer<uint> counter : register(u1); [numthreads(1, 1, 1)] void Process( int3 TId : SV_DispatchThreadID ) { int ind = TId.x; Sector sector = inputData[ind]; if(sector.lod != 0){ sector.borderTessFactor[0] = GetNeibTessFactor(sector, 0); //Bottom sector.borderTessFactor[1] = GetNeibTessFactor(sector, 1); //Left sector.borderTessFactor[2] = GetNeibTessFactor(sector, 2); //Top sector.borderTessFactor[3] = GetNeibTessFactor(sector, 3); //Right int c = counter.IncrementCounter(); int dataSize = 56; outputData.Store(c * dataSize + 0, asuint(sector.startPos.x)); outputData.Store(c * dataSize + 4, asuint(sector.startPos.y)); outputData.Store(c * dataSize + 8, asuint(sector.startPos.z)); outputData.Store(c * dataSize + 12, asuint(sector.vec1.x)); outputData.Store(c * dataSize + 16, asuint(sector.vec1.y)); outputData.Store(c * dataSize + 20, asuint(sector.vec1.z)); outputData.Store(c * dataSize + 24, asuint(sector.vec2.x)); outputData.Store(c * dataSize + 28, asuint(sector.vec2.y)); outputData.Store(c * dataSize + 32, asuint(sector.vec2.z)); outputData.Store(c * dataSize + 36, asuint(sector.borderTessFactor[0])); outputData.Store(c * dataSize + 40, asuint(sector.borderTessFactor[1])); outputData.Store(c * dataSize + 44, asuint(sector.borderTessFactor[2])); outputData.Store(c * dataSize + 48, asuint(sector.borderTessFactor[3])); outputData.Store(c * dataSize + 52, asuint(sector.lod)); } } 

GetNeibTessFactor関数のコヌドは、察応するCPUずほが同じです。唯䞀の違いは、近​​隣ぞのポむンタではなく近隣のむンデックスを䜿甚するこずです。outputDataバッファヌはRWByteAddressBuffer型であるため、Storeメ゜ッドuintアドレス、uint倀を䜿甚しお操䜜したす。dataSize倉数の倀は、D3D11_INPUT_PER_INSTANCE_DATAクラスの頂点デヌタのサむズず等しく、頂点の説明はセクション10にありたす。䞀般に、これはC / C ++のポむンタヌを䜿甚した埓来の䜜業です。2぀のシェヌダヌを実行した埌、outputDataをInstanceBufferずしお䜿甚できたす。レンダリングプロセスは次のようになりたす

 Utils::DirectX::SetPrimitiveStream({vb, outputData}, ib, {vertexSize, instanceByteSize}, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST); DeviceKeeper::GetDeviceContext()->CopyStructureCount(indirectArgs, 4, counterUAW); Shaders::Apply(terrainShaders, [&]() { DeviceKeeper::GetDeviceContext()->DrawIndexedInstancedIndirect(indirectArgs, 0); }); Utils::DirectX::SetPrimitiveStream({nullptr, nullptr}, nullptr, {0, 0}); 

DrawIndexedInstancedIndirectおよびCopyStructureCountメ゜ッドの詳现に぀いおは、付録2を参照しおください。

12.カメラ


簡単なFPSファヌストパヌ゜ンシュヌタヌカメラをモデル化する方法はご存じでしょう。私はこのシナリオに埓っお行動したす




私たちの堎合、状況はやや耇雑です-最初に、惑星の䞭心に察しお移動する必芁があり、2番目に、ベクトル0、1、0の代わりに基底を構築するずきに、珟圚の点で球の法線を䜿甚する必芁がありたす。望たしい結果を達成するために、2぀のベヌスを䜿甚したす。前者によれば、䜍眮が倉わり、埌者はカメラの向きを説明したす。基底は盞互䟝存しおいたすが、最初に䜍眮の基底を蚈算するため、それから始めたす。初期䜍眮ベヌスpDir、pUp、pRightず、距離を移動する方向ベクトルvDirがあるずしたす。たず、vDirの投圱をpDirおよびpRightで蚈算する必芁がありたす。それらを远加するず、曎新された方向ベクトルが埗られたす図21。




図21 projDirを取埗するための芖芚的なプロセス

次に、このベクトル



に沿っお移動したす。Pはカメラの䜍眮、mFずmSは係数です。これは、前方たたは偎方に移動する必芁がある量を意味したす。

PNは球䜓に属さないため、PNを新しいカメラ䜍眮ずしお䜿甚するこずはできたせん。代わりに、ポむントPNで球の法線を芋぀け、この法線はベクトルupの新しい倀になりたす。今、私たちは曎新された基瀎を圢成するこずができたす

 Vector3 nUp = Vector3::Normalize(PN - spherePos); Vector3 nDir = projDir Vector3 nRight = Vector3::Normalize(Vector3::Cross(pUp, pDir)) 

ここで、spherePosは球の䞭心です。

各ベクトルを他の2぀のベクトルに盎亀させる必芁がありたす。ベクトル積の特性に埓っお、nRightはこの条件を満たしたす。 nUpずnDirで同じこずを達成するために残っおいたす。これを行うには、nDirをnUpに投圱し、結果のベクトルをnDirから枛算したす図22。図22




nUpに察するnDirの盎亀化nUp

でも同じこずができたすが、方向が倉わるため、この堎合は受け入れられたせん。次に、nDirを正芏化し、方向の曎新された正芏盎亀基底を取埗したす。

2番目の重芁なステップは、オリ゚ンテヌションの基瀎を構築するこずです。䞻な問題は、方向ベクトルを取埗するこずです。最も適切な解決策は、極角a、方䜍角b、および原点からの距離が1に等しい点を球面座暙からデカルト座暙に倉換するこずです。極角がれロに等しいポむントに察しおこのような遷移を行う堎合にのみ、ベクトルが芋䞊げられたす。角床をむンクリメントし、そのようなベクトルが前方を向くず仮定するため、これは私たちには完党に適しおいたせん。 90床の通垞の角床シフトで問題は解決したすが、角床シフトルヌルを䜿甚するずより゚レガントに



なりたす。その結果、次



が埗られたす。ここで、aは極角、bは方䜍角です。

この結果は、私たちに完党に適しおいるわけではありたせん-䜍眮に基づいお方向ベクトルを構築する必芁がありたす。 vDirの方皋匏を曞き盎したしょう。



宇宙飛行士のようなものはすべお、この方向に非垞に倚く、その方向に非垞に倚くのものを持っおいたす。ここで、暙準基底のベクトルをpDir、pUp、およびpRightに眮き換えるず、必芁な方向が埗られるこずは明らかです。このように



同じこずを行列乗算の圢で想像できたすが、



ベクトルvUpは最初はpUpず等しくなりたす。 vUpずvDirのベクトル積を蚈算するこずでvRightを取埗し、vUpを



残りの基底ベクトルに盎亀させたす。原理はnDirを䜿甚するずきず同じで



、ベヌスを芋぀けたした-カメラの䜍眮を蚈算するために残っおいたす。これは



、spherePosが球䜓の䞭心、sphereRadiusが球䜓の半埄、heightが球䜓の衚面からの高さである堎合に行われたす。説明したカメラのコヌドを瀺したす。

 float moveFactor = 0.0f, sideFactor = 0.0f, heightFactor = 0.0f; DirectInput::GetInsance()->ProcessKeyboardDown({ {DIK_W, [&](){moveFactor = 1.0f;}}, {DIK_S, [&](){moveFactor = -1.0f;}}, {DIK_D, [&](){sideFactor = 1.0f;}}, {DIK_A, [&](){sideFactor = -1.0f;}}, {DIK_Q, [&](){heightFactor = 1.0f;}}, {DIK_E, [&](){heightFactor = -1.0f;}} }); if(moveFactor != 0.0f || sideFactor != 0.0f){ Vector3 newDir = Vector3::Normalize(pDir * Vector3::Dot(pDir, vDir) + pRight * Vector3::Dot(pRight, vDir)); Point3F newPos = pos + (newDir * moveFactor + pRight * sideFactor) * Tf * speed; pDir = newDir; pUp = Vector3::Normalize(newPos - spherePos); pRight = Vector3::Normalize(Vector3::Cross(pUp, pDir)); pDir = Vector3::Normalize(pDir - pUp * Vector3::Dot(pUp, pDir)); pos = spherePos + pUp * (sphereRadius + height); angles.x = 0.0f; } if(heightFactor != 0.0f){ height = Math::Saturate(height + heightFactor * Tf * speed, heightRange); pos = spherePos + pUp * (sphereRadius + height); } DirectInput::MouseState mState = DirectInput::GetInsance()->GetMouseDelta(); if(mState.x != 0 || mState.y != 0 || moveFactor != 0.0f || sideFactor != 0.0f){ if(mState.x != 0) angles.x = angles.x + mState.x / 80.0f; if(mState.y != 0) angles.y = Math::Saturate(angles.y + mState.y / 80.0f, RangeF(-Pi * 0.499f, Pi * 0.499f)); vDir = Vector3::Normalize(pRight * sinf(angles.x) * cosf(angles.y) + pUp * -sinf(angles.y) + pDir * cosf(angles.x) * cosf(angles.y)); vUp = pUp; vRight = Vector3::Normalize(Vector3::Cross(vUp, vDir)); vUp = Vector3::Normalize(vUp - vDir * Vector3::Dot(vDir, vUp)); } viewMatrix = Matrix4x4::Inverse({{vRight, 0.0f}, {vUp, 0.0f}, {vDir, 0.0f}, {pos, 1.0f}}); 

䜍眮の基準を曎新した埌、angles.xをれロにするこずに泚意しおください。これは重芁です。芖野角を同時に倉曎し、球䜓の呚りを移動するこずを想像しおみたしょう。たず、方向ベクトルをpDirずpRightに投圱し、オフセットnewPosを取埗しお、それに基づいお䜍眮の基底を曎新したす。2番目の条件も機胜し、オリ゚ンテヌションベヌスの曎新を開始したす。しかし、pDirずpRightはvDirに応じおすでに倉曎されおいるため、方䜍角angles.xをリセットせずに、回転はより「クヌル」になりたす。

おわりに


読者が蚘事に興味を持っおくれたこずに感謝したす。その䞭に含たれおいる情報が圌にずっお利甚可胜であり、興味深く有甚であったこずを願っおいたす。あなたは私に電子メヌルalexwin32@mail.ruで提案やコメントを送るか、コメントの圢で私を残すこずができたす。

成功を祈っおいたす

付録1
InstanceDataStepRate , D3D11_INPUT_PER_VERTEX_DATA D3D11_INPUT_PER_INSTANCE_DATA. — . « ?» — . . — , 99 . :

 UINT colorsRate = 99 / 3; std::vector<D3D11_INPUT_ELEMENT_DESC> meta = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"WORLD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"WORLD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"WORLD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"WORLD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 48, D3D11_INPUT_PER_INSTANCE_DATA, 1}, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 2, 0, D3D11_INPUT_PER_INSTANCE_DATA, colorsRate}, }; 

, , 33 «». 33 , 33 — .. . , , c D3D11_USAGE_IMMUTABLE. , , GPU , . :

 matricesTb = Utils::DirectX::CreateBuffer(sizeof(Matrix4x4) * 99, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); colorsTb = Utils::DirectX::CreateBuffer(colors, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE, 0); 

( — , )

 Utils::DirectX::Map<Matrix4x4>(matricesTb, [&](Matrix4x4 *Data) { //         //    ..  ,    //         }); 

,

付録2
DrawIndexedInstanced() DrawIndexedInstancedIndirect() , , DrawIndexedInstanced(). D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS. :

 //indicesCnt - - ,     //instancesCnt - - "",     std::vector<UINT> args = { indicesCnt, //IndexCountPerInstance instancesCnt,//InstanceCount 0,//StartIndexLocation 0,//BaseVertexLocation 0//StartInstanceLocation }; D3D11_BUFFER_DESC bd = {}; bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof(UINT) * args.size(); bd.BindFlags = 0; bd.CPUAccessFlags = 0; bd.MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; bd.StructureByteStride = 0; ID3D11Buffer* buffer; D3D11_SUBRESOURCE_DATA initData = {}; initData.pSysMem = &args[0]; HR(DeviceKeeper::GetDevice()->CreateBuffer(&bd, &initData, &buffer));   DrawIndexedInstancedIndirect(): DeviceKeeper::GetDeviceContext()->DrawIndexedInstancedIndirect(indirectArgs, 0); 

, . これはどのように䜿甚できたすか , GPU. — Compute AppendStructuredBuffer, . CopyStructureCount() «», DrawIndexedInstancedIndirect()

付録3
, X a, z — Z :



. , . :



:



? . , ( t >= 0):



X



Y



, (2, 3),



P(t) :



« (3, 2) t (2, 3)». :



X



Y



. : « (3, 2), ».

付録4
F(H), [Hmin, Hmax] 0 1, F(Hmin) = 0 F(Hmax) = 1.







F



, 0 1 . , — . :











D(F(H))


Source: https://habr.com/ru/post/J335588/


All Articles