7

私はソフトウェアラスタライザーを作成していますが、ちょっとした問題に遭遇しました。パースペクティブが正しいテクスチャマッピングを機能させることができないようです。

私のアルゴリズムは、最初に座標を並べ替えてプロットすることyです。これにより、最高点、最低点、および中心点が返されます。次に、デルタを使用してスキャンラインを横断します。

// ordering by y is put here

order[0] = &a_Triangle.p[v_order[0]];
order[1] = &a_Triangle.p[v_order[1]];
order[2] = &a_Triangle.p[v_order[2]];

float height1, height2, height3;

height1 = (float)((int)(order[2]->y + 1) - (int)(order[0]->y));
height2 = (float)((int)(order[1]->y + 1) - (int)(order[0]->y));
height3 = (float)((int)(order[2]->y + 1) - (int)(order[1]->y));

// x 

float x_start, x_end;
float x[3];
float x_delta[3];

x_delta[0] = (order[2]->x - order[0]->x) / height1;
x_delta[1] = (order[1]->x - order[0]->x) / height2;
x_delta[2] = (order[2]->x - order[1]->x) / height3;

x[0] = order[0]->x;
x[1] = order[0]->x;
x[2] = order[1]->x;

order[0]->y次に、からをレンダリングし、とをデルタorder[2]->yで増やします。上部をレンダリングする場合、デルタはとです。下部をレンダリングする場合、デルタはとです。次に、スキャンライン上のx_startとx_endの間を線形補間します。UV座標は同じ方法で補間され、yの順に、開始と終了から始まり、各ステップにデルタが適用されます。x_startx_endx_delta[0]x_delta[1]x_delta[0]x_delta[2]

これは、遠近法で正しいUVマッピングを実行しようとする場合を除いて正常に機能します。基本的なアルゴリズムは、各頂点に対してUV/zとを取り、それらの間を補間することです。1/zピクセルごとに、UV座標はになりUV_current * z_currentます。ただし、これは結果です。

代替テキスト

逆の部分は、デルタが反転する場所を示します。ご覧のとおり、2つの三角形は両方とも地平線の異なるポイントに向かっているように見えます。

空間内のある点でZを計算するために使用するものは次のとおりです。

float GetZToPoint(Vec3 a_Point)
{
    Vec3 projected = m_Rotation * (a_Point - m_Position);

    // #define FOV_ANGLE 60.f
    // static const float FOCAL_LENGTH = 1 / tanf(_RadToDeg(FOV_ANGLE) / 2);
    // static const float DEPTH = HALFHEIGHT * FOCAL_LENGTH; 
    float zcamera = DEPTH / projected.z;

    return zcamera;
}

私は正しいですか、それはazバッファの問題ですか?

4

3 に答える 3

5

ZBufferはそれとは何の関係もありません。

ZBufferは、三角形が重なっていて、三角形が正しく描画されていることを確認したい場合にのみ役立ちます(たとえば、Zで正しく順序付けられている)。ZBufferは、三角形のすべてのピクセルについて、以前に配置されたピクセルがカメラに近いかどうかを判断し、近い場合は、三角形のピクセルを描画しません。

重ならない2つの三角形を描いているので、これが問題になることはありません。

固定小数点でソフトウェアラスタライザーを一度作成しましたが(携帯電話用)、ラップトップにソースがありません。それで、今夜、私がそれをどのようにしたかを確認させてください。本質的に、あなたが持っているものは悪くありません!このようなことは、非常に小さなエラーが原因である可能性があります

これをデバッグする際の一般的なヒントは、いくつかのテスト三角形(左側の傾斜、右側の傾斜、90度の角度など)を用意し、デバッガーを使用してステップスルーし、ロジックがケースをどのように処理するかを確認することです。

編集:

私のラスタライザーの疑似コード(U、V、およびZのみが考慮されます...グーローシェーディングも実行する場合は、UおよびVおよびZに対して実行するのと同様に、RGおよびBに対してもすべてを実行する必要があります。

三角形は2つの部分に分解できるという考え方です。上部と下部。上部はy[0]からy[1]で、下部はy[1]からy[2]です。両方のセットについて、補間するステップ変数を計算する必要があります。以下の例は、上部の実行方法を示しています。必要に応じて、下部も供給できます。

以下の「擬似コード」フラグメントの下部に必要な補間オフセットをすでに計算していることに注意してください。

  • coords(x、y、z、u、v)を、coord [0] .y <coord [1] .y <coord[2].yの順序で最初に並べ替えます。
  • 次に、2セットの座標が同一であるかどうかを確認します(xとyのみを確認します)。もしそうなら、描画しないでください
  • 例外:三角形の上部は平らですか?もしそうなら、最初の勾配は無限になります
  • 例外2:三角形の底は平らですか(はい、三角形もこれらを持つことができます; ^))、最後の勾配も無限になります
  • 2つの勾配(左側と右側)を計算します
    leftDeltaX =(x [1] --x [0])/(y [1] -y [0])およびrightDeltaX =(x [2] --x [0])/ (y [2] -y [0])
  • 三角形の2番目の部分は、次の条件に従って計算されます。三角形の左側が実際に左側にあるかどうか(またはスワッピングが必要かどうか)

コードフラグメント:

 if (leftDeltaX < rightDeltaX)
 {
      leftDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
      rightDeltaX2 = rightDeltaX
      leftDeltaU = (u[1]-u[0]) / (y[1]-y[0]) //for texture mapping
      leftDeltaU2 = (u[2]-u[1]) / (y[2]-y[1])
      leftDeltaV = (v[1]-v[0]) / (y[1]-y[0]) //for texture mapping
      leftDeltaV2 = (v[2]-v[1]) / (y[2]-y[1])
      leftDeltaZ = (z[1]-z[0]) / (y[1]-y[0]) //for texture mapping
      leftDeltaZ2 = (z[2]-z[1]) / (y[2]-y[1])
 }
 else
 {
      swap(leftDeltaX, rightDeltaX);
      leftDeltaX2 = leftDeltaX;
      rightDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
      leftDeltaU = (u[2]-u[0]) / (y[2]-y[0]) //for texture mapping
      leftDeltaU2 = leftDeltaU
      leftDeltaV = (v[2]-v[0]) / (y[2]-y[0]) //for texture mapping
      leftDeltaV2 = leftDeltaV
      leftDeltaZ = (z[2]-z[0]) / (y[2]-y[0]) //for texture mapping
      leftDeltaZ2 = leftDeltaZ
  }
  • currentLeftXとcurrentRightXの両方をx[0]に設定します
  • currentLeftUをleftDeltaUに、currentLeftVをleftDeltaVに、currentLeftZをleftDeltaZに設定します。
  • 最初のY範囲の開始と終了を計算します。startY=ceil(y [0]); endY = ceil(y [1])
  • サブピクセルの精度のためにyの小数部分のプレステップx、u、v、z(これはフロートにも必要だと思います)私の固定小数点アルゴリズムでは、ラインとテクスチャがはるかに細かいステップで移動しているような錯覚を与えるためにこれが必要でしたディスプレイの解像度)
  • xがy[1]にあるべき場所を計算します:halfwayX =(x [2]-x [0])*(y [1]-y [0])/(y [2]-y [0])+ x [0]そしてUとVとzについても同じ:halfwayU =(u [2] -u [0])*(y [1]-y [0])/(y [2]-y [0])+ u [0]
  • そして、halfwayXを使用して、UとVおよびzのステッパーを計算します。if(halfwayX --x [1] == 0){slopeU = 0、slopeV = 0、slopeZ = 0} else {slopeU =(halfwayU --U [1 ])/(halfwayX --x [1])} //(vとzについても同じ)
  • Y上部のクリッピングを実行します(三角形の上部が画面から外れている(またはクリッピング長方形から外れている)場合に、描画を開始する場所を計算します)
  • y=startYの場合; y <endY; y ++){
    • Yは画面の下を過ぎていますか?レンダリングを停止します!
    • 最初の水平線leftCurX=ceil(startx);のstartXとendXを計算します。leftCurY = ceil(endy);
    • 画面の左側の水平方向の境界線(またはクリッピング領域)に描画する線をクリップします
    • 宛先バッファーへのポインターを準備します(毎回配列インデックスを介して行うのは遅すぎます)unsigned int buf = destbuf +(yピッチ)+ startX; (24ビットまたは32ビットのレンダリングを行う場合はunsigned int)ここでZBufferポインターも準備します(これを使用している場合)
    • for(x = startX; x <endX; x ++){
      • パースペクティブテクスチャマッピングの場合(bilineair補間を使用しない場合は、次のようにします):

コードフラグメント:

         float tv = startV / startZ
         float tu = startU / startZ;
         tv %= texturePitch;  //make sure the texture coordinates stay on the texture if they are too wide/high
         tu %= texturePitch;  //I'm assuming square textures here. With fixed point you could have used &=
         unsigned int *textPtr = textureBuf+tu + (tv*texturePitch);   //in case of fixedpoints one could have shifted the tv. Now we have to multiply everytime. 
         int destColTm = *(textPtr);  //this is the color (if we only use texture mapping)  we'll be needing for the pixel
  • ダミーライン
    • ダミーライン
      • ダミーライン
      • オプション:この座標で以前にプロットされたピクセルが私たちのピクセルよりも高いか低いかをzbufferで確認します。
      • ピクセルをプロットする
      • startZ+=スロープZ; startU + = slotU; startV+=スロープV; //すべての補間器を更新します
    • }xループの終わり
    • leftCurX + = leftDeltaX; rightCurX + = rightDeltaX; leftCurU + = rightDeltaU; leftCurV + = rightDeltaV; leftCurZ + = rightDeltaZ; //Y補間器を更新します
  • }yループの終わり

    //これで最初の部分は終わりです。これで、三角形の半分が描画されました。上から中央のY座標まで。//基本的にまったく同じことを行いますが、三角形の下半分に対して(他の補間器のセットを使用して)

「ダミーライン」について申し訳ありません。マークダウンコードを同期させるために必要でした。(意図したとおりにすべてを整理するのにしばらく時間がかかりました)

これがあなたが直面している問題を解決するのに役立つかどうか私に知らせてください!

于 2010-01-18T11:10:21.237 に答える
0

私があなたの質問に答えられるかどうかはわかりませんが、当時私が読んだソフトウェアレンダリングに関する最高の本の1つは、MichaelAbrashによるオンラインのGraphicsProgrammingBlackBookです。

于 2010-01-14T23:32:29.187 に答える
0

補間する場合は、ではなく、1/zを掛ける必要があります。あなたがこれを持っていると仮定して:UV/zz1/z

UV = UV_current * z_current

z_current補間し1/zている場合は、次のように変更する必要があります。

UV = UV_current / z_current

次に、z_currentのような名前に変更することをお勧めしますone_over_z_current

于 2010-01-19T23:29:08.097 に答える