繁体   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