二重線形補間を使用して地形の正確な高さを計算する

6533 ワード

に質問
地形を使ったゲームを作るときは、地形の定点の正確な高さを知る必要があります.たとえば、地形上でモデルを移動する場合(チュートリアル4-17を参照)、カーソルと地形の衝突が検出された場合(次のチュートリアル)、またはカメラが地形に衝突しないようにする場合(チュートリアル2-6を参照).
前のチュートリアルでは、地形の各頂点の3 D位置を定義したので、これらの点の高さを取得するのは簡単です.これらの頂点間に位置する位置では、補間方法を使用してこの位置の正確な高さを取得する必要があります.
ソリューション
高さの点が地形の頂点と衝突していることを知りたい場合は、地形上の点の正確な高さを知っています.この点が頂点に衝突していない場合は、この点が地形の三角形にあることを示します.三角形は平面なので、三角形の3つの頂点間を補間することで、任意の点の正確な高さを得ることができます.
さぎょうげんり
まず、X座標とZ座標から、三角形の3つの頂点の高さを補間することで取得できる点の対応するY座標を知りたいです.
これは、まず点がどの三角形にあるのかを見つけるのは容易ではないことを意味します.しかし、まず補間について紹介したいと思います.
リニア補間(Linear Interpolation)
分離されたデータのみを処理し、分離点間のいくつかの値を知りたい場合は、あるタイプの補間が必要です.この場合、図5〜図17の座標に示す.いくつかの分離された(整数)X値に対して、Y値を知っています.X=2の場合、Y=10、X=3の場合、Y=30になることを知っています.しかし、X=2.7のY値を知らない.
図5-17線形補間:単純な一般的な例
線形補間を使用すると、図5-17に示すように、2点を接続するセグメントからX=2.7の対応するY値が見つかります.線形補間を使用して、2点の線分を接続してX=2.7の対応するY値を見つけます.線形補間は、常にXを0と1の間に表現し、0はXの最小値(この例では2)に対応し、1はXの最大値(この例では3)に対応します.この例では、X=2.7のY値を見つけたいと思っています.結果は0.7で、「2と3から70%です」という意味です.
図5−17の左図では、0%がY値10100%がY値20に対応するので、70%がY=17に対応する.これはわかりやすいですが、右図の状況はどうですか.14は、13〜16の33%であるため、0.33に対応する.しかし、35と46の33%はいくらですか.明らかに、コードが結果を計算することを望んでいます.
まず、0と1に対応する値を見つけるコードがあります.X値から、まず最小のX値を減算し、最小値を0にします.次に、最大値を1にスケールし、最大値と最小値の差で除算することで実現できます.
図5-17左図の作り方:2.7→(2.7-min)/(max-min)=(2.7-2)/(3-2)=0.7
次に、逆演算を行って対応するY値を取得します.まず、この値をスケールし(最大値と最小値の差を乗算することによって)、最小のY値に加算します.
0.7* (maxY-min Y)+minY=0.7*(20-10)+10=0.7*10+10=17
ここでは、図5-17の左図の簡単な例のルールを採用しますが、この方法を使用して任意の線形補間を計算することができます.図5-17の右図のもっと難しい例を見てみましょう.この場合、X=13がY=35、X=16がY=46に対応していることを知っていますが、X=14がY値に対応していることを知りたいです.したがって、まず0と1の間の対応する値を取得します.
14→(14-minX)/(maxX-minX) =(14-13)/(16-13)=0.33
対応する値を知ると、対応するY値を取得する準備ができています.
0.33* (maxY-minY)+minY=0.33*(46-35)+35=0.33*11+35=3.67+35=38.67
最後に、浮動小数点計算が必要です.図5-17の右図には、X=14がY=38.67に対応していることが示されている.実際、ほとんどの補間計算は浮動小数点数を返します.
テクニック:XNAは、Vector 2、Vector 3、またはVector 4の補間を計算する機能を提供します.例えば、どのVector 2が(5,8)と(2,9)の間の70%にあるかを取得したい場合は、Vector 2を使用することができます.Lerp(new Vector2(5,8), new Vector2(2,9), 0.7f).
にじゅうせんけい補間
地形の例では、すべての(X,Z)値に対して、頂点を定義し、その正確な高さを知っています.これらの独立した頂点間のすべての(X,Z)値に対して、正確なY値が分からないので、補間する必要があります.今回はXとZを含む0と1の間の値を取得する必要があります.
これらの値があれば、2段階に分けて正確なY値を算出することができます.
対応する値の取得
任意の(X,Z)座標を指定すると、地形上の正確なY高さを見つける必要があります.まず前の式を使って対応するXとZの値を見つけますが、今回は3次元空間で2回使う必要があります.
int xLower = (int)xCoord; 
int xHigher = xLower + 1; 
float xRelative = (xCoord - xLower) / ((float)xHigher - (float)xLower); 
int zLower = (int)zCoord; 
int zHigher = zLower + 1; 
float zRelative = (zCoord - zLower) / ((float)zHigher- (float)zLower);

地形のXとZの整数値ごとに頂点を定義するので、正確なY値を知っています.したがって、各Xの浮動小数点数について、整数に変換して最小のX値を取得します(たとえば、2.7が2になります).この値に1を加えて最大X値を取得する(2.7は3を最大値とする).境界がわかり、前の式で対応する値を見つけやすいです.Z値の求め方は似ています.
minYとmaxYの値を取得
0と1の対応する値が分かりました.次は正確なY値を見つけます.ただし、まずminYとmaxYの値を知る必要があります.これらの値は頂点の高さを表します.点がどの三角形にあるかを知る必要があります.Y値としてどの頂点の高さを使用するかを知る必要があります.
点PのX座標とZ座標を知っているので、点の周りの4つの頂点を知っていて、Y値を簡単に取得できます.
float heightLxLz = heightData[xLower, zLower]; 
float heightLxHz = heightData[xLower, zHigher]; 
float heightHxLz = heightData[xHigher, zLower]; 
float heightHxHz = heightData[xHigher, zHigher]; 

LxHzは「低X座標、高Z座標」決定(X,Z)を表す.
点がどの三角形で地形の2つの三角形を描くのに使用されますか.図5〜18に示すように、この2つの三角形を定義する方法は2つある.図に示すように、三角形を描く方法はP点の高さに影響します.
図5-18 4つの頂点から2つの三角形を描く2つの方法
4つの頂点には同じ座標がありますが、2つの場合の点の高さは異なり、図には明らかな違いがあります.
私が議論する理由に基づいて、より良い方法は図5-18の右図です.
この回転方式を使用すると、点がどの三角形の上にあるかを簡単に決定できます.2つの三角形間の境界は対角線によって与えられる.右図では、xRelative+zRelativeが1の場合、この線はX座標とZ座標を持つ点に対応します.
例えば、この点が4つの点の中央にある場合、図5〜図18に示すように、xRelativeおよびzRelativeはいずれも0.5 fであるので、和は1であり、対角線上で説明される.この点が左に偏っている場合、xRelativeは小さくなり、1より小さくなり、Z座標にも似ています.したがって、和が1未満の場合、(X,Z)座標は左下隅の三角形内に位置する.そうでなければ、この点は右上隅の三角形内にあります.
bool pointAboveLowerTriangle = (xRelative + zRelative < 1); 

なお、図5〜図16に定義されたすべての三角形は、図5〜図18の右図の形式で描かれている.
正確な高さを取得
対応する高さ、4つの周囲の頂点の高さと点がどの三角形にあるかを知ると、正確な高さを計算することができます.
点が左下の三角形の場合、pointAboveLowertiangleがtrueの場合、二重線形補間を使用して三角形の任意の点の高さを取得するコードは次のとおりです.
finalHeight = heightLxLz; 
finalHeight += zRelative * (heightLxHz - heightLxLz); 
finalHeight += xRelative * (heightHxLz - heightLxLz); 

先に説明した単一補間の方法に従ってlowestXのY値から開始する.これは「ダブル」補間なので、lowestXlowestZのY値から始めます.
単一補間では、maxY間に高さ差を追加し、対応するX値を乗算します.ダブル補間では、zRelativeとxRelativeに乗ります.
言い換えれば、左下の頂点の高さから、この高さに対して、この頂点とより高いZ座標を持つ頂点との高さ差を追加し、2番目の頂点からのZ座標の接近度を乗じます.最後の行のコードは似ています.この高さに対して、左下の頂点と右下の頂点の高さの差を追加し、右下の頂点からのX座標の近接度を乗算します.
この点が右上三角形の内部にある場合、pointAboveLowerTriangleがfalseである場合は、次のコードが必要です.
finalHeight = heightHxHz; 
finalHeight += (1.0f - zDifference) *(heightHxLz - heightHxHz); 
finalHeight += (1.0f - xDifference) * (heightLxHz - heightHxHz); 

高さから、右上の頂点から、同じ手順に従います.高さ差を追加し、対応する距離を乗算します.
コード#コード#
この方法には、前に説明したすべてのコードが含まれています.任意の(X,Z)座標に基づいて,この方法は整数でも浮動小数点数でもこの点の正確な高さを返す.まず、この点が地形上にあるかどうかをチェックします.そうでない場合は、既定の高さ10を返します.
public float GetExactHeightAt(float xCoord, float zCoord) 
{
    bool invalid = xCoord < 0; 
    invalid |= zCoord < 0; 
    invalid |= xCoord > heightData.GetLength(0) - 1; 
    invalid |= zCoord > heightData.GetLength(1) - 1; 
    
    if (invalid) 
        return 10; 
    
    int xLower = (int)xCoord; 
    int xHigher = xLower + 1; 
    float xRelative = (xCoord - xLower) / ((float)xHigher - (float)xLower); 
    int zLower = (int)zCoord; 
    int zHigher = zLower + 1; 
    float zRelative = (zCoord - zLower) / ((float)zHigher - (float)zLower); 
    float heightLxLz = heightData[xLower, zLower]; 
    float heightLxHz = heightData[xLower, zHigher]; 
    float heightHxLz = heightData[xHigher, zLower]; 
    float heightHxHz = heightData[xHigher, zHigher]; 
    
    bool pointAboveLowerTriangle = (xRelative + zRelative < 1); 
    float finalHeight; 
    if (pointAboveLowerTriangle ) 
    { 
        finalHeight = heightLxLz; 
        finalHeight += zRelative * (heightLxHz - heightLxLz); 
        finalHeight += xRelative * (heightHxLz - heightLxLz); 
    }
    else 
    {
        finalHeight = heightHxHz; 
        finalHeight += (1.0f - zRelative) * (heightHxLz - heightHxHz); 
        finalHeight += (1.0f - xRelative) * (heightLxHz - heightHxHz); 
    }
    return finalHeight; }