簡體   English   中英

使用循環遍歷三角形內的所有點

[英]Traversing all the points inside a triangle using loops

以下是3D中三角形的x,y和z坐標的三個頂點:

-0.2035416, 0.1107585, 0.9516008 (vertex A)
-0.0334390, -0.2526040, 0.9751212 (vertex B)
0.2569092, 0.0913718, 0.9817184 (Vertex C)

投影平面分為(高度*寬度)像素的網格。

我想手動遍歷投影平面內三角形內的每個像素,從三角形的底部到頂部,並在c ++中在屏幕上打印三角形內的每個像素坐標 說,我已經找到了三角形的頂部和底部頂點。 但是現在,我將如何從下到上遍歷並打印每個像素坐標? 這背后的邏輯是什么?

我有一個使兩個嵌套的for循環(如下所示)的想法,但是在循環內部我將做什么? x和y每次增加后,如何使側面移動?

for (int y = ymin; y <= ymax; ++y) {
   for (int x = xmin; x <= xmax; ++x) {

  //what to to here?

    }
 }

遍歷演示:

#include <iostream>

template <size_t kD>
class Vector{
public:
  template <class... Args>
  Vector(double coord, Args&&... args) {
    static_assert( sizeof...(args)+1 == kD, "Unmatched vector dimension" );
    InitCoord(0, coord, std::forward<Args>(args)...);
  }

  Vector(const Vector &) = default;
  Vector &operator=(const Vector &) = default;

  double &operator[](const size_t i) {
    return coord_[i];
  }
  double operator[](const size_t i) const {
    return coord_[i];
  }

  friend Vector<kD> operator-(const Vector<kD> &A, const Vector<kD> &B) {
    Vector v;
    for (size_t i=0; i<kD; ++i)
      v[i] = A[i]-B[i];
    return v;
  }
private:
  Vector() = default;

  template <class... Args>
  void InitCoord(const int pos, double coord, Args&&... args) {
    coord_[pos] = coord;
    InitCoord(pos+1, std::forward<Args>(args)...);
  }

  void InitCoord(const int pos) {}

  double coord_[kD];
};

class Line {
public:
  Line(const double x1, const double y1, const double x2, const double y2)
      : x1_(x1), y1_(y1), x2_(x2), y2_(y2) {}

  Line(const Vector<2> A, const Vector<2> B)
      : x1_(A[0]), y1_(A[1]), x2_(B[0]), y2_(B[1]) {}

  double operator()(const double x, const double y) const {
    return (y-y1_)*(x2_-x1_) - (x-x1_)*(y2_-y1_);
  }

  int_fast8_t Sign(const double x, const double y) const {
    return Signum( (y-y1_)*(x2_-x1_) - (x-x1_)*(y2_-y1_) );
  }
private:
  int_fast8_t Signum(const double x) const {
    return (0.0 < x) - (x < 0.0);
  }

  const double x1_,y1_;
  const double x2_,y2_;
};

void Transpos(Vector<2> &v) {
  v[0] = (v[0]+1)/2*720; // col
  v[1] = (v[1]+1)/2*480; // row
}

double CalculateZ(const Vector<2> &D, const Vector<2> &AB, const Vector<2> &AC,
    const Vector<3> &AB3, const Vector<3> &AC3) {
  const double b = (D[1]*AB[0]-D[0]*AB[1]) / (AC[1]*AB[0]-AC[0]*AB[1]);
  const double a = AB[0]==0 ? (D[1]-b*AC[1])/AB[1] : (D[0]-b*AC[0])/AB[0];

  std::cout << a << " " << b << std::endl;

  return a*AB3[2]+b*AC3[2];
}

int main()
{
  const auto A3 = Vector<3>(0.0, 0.0, 7.0);
  const auto B3 = Vector<3>(0.0, 0.3, 9.0);
  const auto C3 = Vector<3>(0.4, 0.0, 1.0);

  const auto AB3 = B3-A3;
  const auto AC3 = C3-A3;
  const auto BC3 = C3-B3;

  // Some projection works here, which I am not good at.
  // A B C store the projected triangle coordiate in the [-1,1][-1,1] area

  auto A = Vector<2>(0.0, 0.0);
  auto B = Vector<2>(0.0, 0.3);
  auto C = Vector<2>(0.4, 0.0);

  Transpos(A);
  Transpos(B);
  Transpos(C);

  const auto AB2 = B-A;
  const auto AC2 = C-A;
  const auto BC2 = C-B;

  const Line AB(A, B);
  const Line AC(A, C);
  const Line BC(B, C);

  const auto signAB = AB.Sign(C[0],C[1]);
  const auto signAC = AC.Sign(B[0],B[1]);
  const auto signBC = BC.Sign(A[0],A[1]);

  // top
  // 0------------720 (col x)
  // |
  // |
  // |
  // |
  // 480 (row y)
  // bottom

  for (int row=480-1; row>=0; --row) {
    for (int col=0; col<720; ++col) {
      if (signAB*AB.Sign(col,row)>=0 && signAC*AC.Sign(col,row)>=0 &&
          signBC*BC.Sign(col,row)>=0 )
        std::cout << row << "," << col << " Z:"
          << CalculateZ(Vector<2>(col, row)-A, AB2, AC2, AB3, AC3) + A3[2]
          << std::endl;
    }
  }

  return 0;
}

投影:

  • 第一空格[-1,1] [-1,1]
  • 第二空間[0,720] [0,480]

假設我們在第一個空格中有一個(x1,y1),然后第二個空格中的(x_,y_)與x _ =(x1 + 1)/ 2 * 720,y _ =(y1 + 1)/ 2 * 480空間。

更概括地說:

first space [xmin,xmax][ymin,ymax]
second space [xmin_,xmax_][ymin_,ymax_]
(x1,y1)
->
( (x1-xmin)/(xmax-xmin)*(xmax_-xmin_)+xmin_ ,  
  (y1-ymin)/(ymax-ymin)*(ymax_-ymin_)+ymin_ )

如果您只想縮放它,不要扭曲它或其他東西...

編輯#1:

  • 感謝@Adrian Colomitchi的出色建議,我改進了演示。
  • Ax Ay Bx By Cx Cy現在是第一個空間中的坐標,然后將它們“轉置”到第二個空間中。 結果, Line AB AC BC現在“在”第二個空間中。 並且相應地修改了兩個循環,它們現在迭代第二個空間的點。

如何從(x,y)中找到z值:

AB代表從A(Ax,Ay)到B(Bx,By)的向量,即AB = BA =(Bx-Ax,By-Ay)。

對於三角形中任何給定的點D(Dx,Dy),將其表示為AD = a AB + b AC:(Dx-Ax,Dy-Ay)= a *(Bx-Ax,By-Ay)+ b *( Cx-Ax,Cy-Ay),其中Cx Cy的Dx Dy Ax Ay Bx是已知的。 找出a和b,然后Dz = a *(Bz-Az)+ b *(Cz-Az)。 可以以相同的方式計算3D空間中的Dx Dy。

編輯#2:

Z值計算已添加到演示中。

我試圖使演示保持簡單,但是計算Z值確實涉及許多變量和計算。 我聲明了一個稱為Vector的新類來管理點和矢量,而Line類則保持不變。

您需要稍微更改內部循環; 不要從xmin轉到xmax。 對於從ymin到ymax的每個y值,都會有兩個完全不同的像素(兩個不同的x值),它們恰好位於三角形的兩個邊緣上。 計算這些點,然后它們之間的所有點都將在三角形內。 而且,您必須處理一些邊緣情況,例如其中一條邊緣是水平的。

首先,您必須將{0,1}范圍(垂直和水平)轉換為像素坐標。 您說的是720x480。 這不是正方形,而是矩形。 如果您決定保持一對一的比例,則會得到一個變形的三角形。 如果沒有,也許您只使用480x480像素。

其次,現在您在像素空間中擁有三個頂點,您可以迭代此像素空間中的每個像素,並確定其是否屬於三角形。 @felix在他的解決方案代碼中發布了用於此工作的函數“ InTriangle”:

if (signAB*AB(i,j)>=0 && signAC*AC(i,j)>=0  && signBC*BC(i,j)>=0 )

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM