【问题标题】:How to draw a Perspective-Correct Grid in 2D如何在 2D 中绘制透视校正网格
【发布时间】:2010-10-06 12:50:27
【问题描述】:

我有一个应用程序,它在图像/照片之上定义了一个真实世界的矩形,当然在 2D 中它可能不是一个矩形,因为你是从一个角度看它。

问题是,假设矩形需要在其上绘制网格线,例如,如果它是 3x5,那么我需要从第 1 面到第 3 面绘制 2 条线,从第 2 面到第 4 面绘制 4 条线。

截至目前,我将每条线分成等距的部分,以获得所有网格线的起点和终点。然而,矩形的角度越大,这些线就越“不正确”,因为离您越远的水平线应该更靠近。

有人知道我应该搜索的算法的名称吗?

是的,我知道您可以在 3D 中执行此操作,但是对于此特定应用程序,我仅限于 2D。

【问题讨论】:

  • 那么一个例子可能是尝试在图片中的窗口上绘制一个矩形?
  • 是的,这就是一个例子
  • 您对这个项目有什么好运吗?我需要一些非常相似的东西!谢谢

标签: algorithm graphics geometry 2d augmented-reality


【解决方案1】:

Here's 解决方案。

基本思想是您可以通过对角连接角找到矩形的正确透视“中心”。两条结果线的交点是您的透视正确中心。从那里您将矩形细分为四个较小的矩形,然后重复该过程。次数取决于您想要的准确度。您可以细分到像素大小以下,以获得有效的完美透视。

然后在您的子矩形中,您只需应用标准未校正的“纹理”三角形或矩形或其他任何东西。

您可以执行此算法,而无需费心构建“真实”3d 世界。如果您确实有一个真实的 3D 世界建模,但您的 texttriangles 没有在硬件中进行透视校正,或者您需要一种高性能的方法来获得透视正确的平面,而不需要每像素渲染技巧,这也很有用。 p>

【讨论】:

  • 此解决方案是否仅适用于 2 的幂的网格?
  • 我想是这样,但如果你是一个足够优秀的数学家,你可能会推导出一个非线性变换矩阵,它反映了源像素和目标像素之间的相同关系。我不是这样的数学家,但我至少知道要研究的神奇词汇,现在,你也知道了。
【解决方案2】:

我认为所选答案不是可用的最佳解决方案。更好的解决方案是将矩形的透视(投影)变换应用于简单网格,如下所示 Matlab 脚本和图像显示。你也可以用 C++ 和 OpenCV 来实现这个算法。

function drawpersgrid
sz      = [ 24, 16 ]; % [x y]
srcpt   = [ 0 0; sz(1) 0; 0 sz(2); sz(1) sz(2)];
destpt  = [ 20 50; 100 60; 0 150; 200 200;];

% make rectangular grid
[X,Y]   = meshgrid(0:sz(1),0:sz(2));

% find projective transform matching corner points
tform   = maketform('projective',srcpt,destpt);

% apply the projective transform to the grid
[X1,Y1] = tformfwd(tform,X,Y);

hold on;

%% find grid

for i=1:sz(2)
    for j=1:sz(1)
        x = [ X1(i,j);X1(i,j+1);X1(i+1,j+1);X1(i+1,j);X1(i,j)];
        y = [ Y1(i,j);Y1(i,j+1);Y1(i+1,j+1);Y1(i+1,j);Y1(i,j)];
        plot(x,y,'b');
    end
end
hold off;

【讨论】:

  • @naglas 这没有给我足够的信息来帮助你。据我所知,脚本运行良好。
  • 我在 Octave 和 Python 中都试过了,得到了基本相同的错误。
  • @naglas 你为什么不用你的 python 代码创建一个问题并让我知道,这样我或其他人也许可以帮助你。
【解决方案3】:

给定绕 y 轴的旋转,特别是如果旋转表面是平面的,则透视是由垂直梯度生成的。这些在视角上逐渐接近。而不是使用对角线来定义四个矩形,它可以在给定二的幂的情况下工作......定义两个矩形,左右。如果继续将表面划分为更窄的垂直部分,它们最终会高于宽度。这可以适应非方形的表面。如果围绕 x 轴旋转,则需要水平渐变。

【讨论】:

    【解决方案4】:

    这是我想出来的几何解决方案。我不知道“算法”是否有名字。

    假设您想首先将“矩形”分成 n 块,其中包含垂直线。

    我们的目标是将点 P1..Pn-1 放在顶线上,我们可以用它来画线穿过它们到左右线相交或平行的点,当这样的点不存在时。

    如果顶线和底线相互平行,只需放置这些点以等距分割角之间的顶线。

    否则将 n 个点 Q1..Qn 放在左边线上,这样这些点和左上角是等距的,并且 i Qi 比 Qj 更靠近左上角。 为了将 Q 点映射到顶线,找到从 Qn 到右上角的线的交点 S,并通过顶线和底线的交点找到与左线平行的线。现在将 S 与 Q1..Qn-1 连接起来。新线与顶线的交点是想要的 P 点。

    对水平线做这个模拟。

    【讨论】:

      【解决方案5】:

      图片:双线性和透视变换示例(注意:上下水平网格线的高度实际上是其余线高度的一半,在两张图上)

      =========================================

      我知道这是一个老问题,但我有一个通用的解决方案,所以我决定发布它,希望它对未来的读者有用。 下面的代码可以绘制任意透视网格,无需重复计算。

      我实际上是从一个类似的问题开始的:绘制一个 2D 透视网格,然后转换下划线图像以恢复透视。

      我开始在这里阅读: http://www.imagemagick.org/Usage/distorts/#bilinear_forward

      然后在这里(Leptonica 图书馆): http://www.leptonica.com/affine.html

      我是否发现了这个:

      当你从某个任意方向看平面上的物体时 有限的距离,你会得到一个额外的“梯形”失真 图片。这是一个投影变换,它保持直线 直线但不保留线之间的角度。这种翘曲 不能用线性仿射变换来描述,事实上 分母中与 x 和 y 相关的项不同。

      正如许多人在此线程中已经指出的那样,转换不是线性的。它涉及求解一个由 8 个方程组成的线性系统(一次)以计算 8 个所需的系数,然后您可以使用它们来变换任意数量的点。

      为了避免在我的项目中包含所有 Leptonica 库,我从中提取了一些代码,删除了所有特殊的 Leptonica 数据类型和宏,修复了一些内存泄漏并将其转换为 C++ 类(主要用于封装原因)它只做一件事: 它将 (Qt) QPointF float (x,y) 坐标映射到相应的透视坐标。

      如果你想让代码适应另一个 C++ 库,唯一需要重新定义/替换的就是 QPointF 坐标类。

      我希望一些未来的读者会发现它有用。 下面的代码分为3部分:

      A.如何使用 genImageProjective C++ 类绘制 2D 透视网格的示例

      B. genImageProjective.h 文件

      C. genImageProjective.cpp 文件

      //============================================================
      // C++ Code Example on how to use the 
      //     genImageProjective class to draw a perspective 2D Grid
      //============================================================
      
      #include "genImageProjective.h"
      
      // Input: 4 Perspective-Tranformed points:
      //        perspPoints[0] = top-left
      //        perspPoints[1] = top-right
      //        perspPoints[2] = bottom-right
      //        perspPoints[3] = bottom-left
      void drawGrid(QPointF *perspPoints)
      {
      (...)
              // Setup a non-transformed area rectangle
              // I use a simple square rectangle here because in this case we are not interested in the source-rectangle,
              //  (we want to just draw a grid on the perspPoints[] area)
              //   but you can use any arbitrary rectangle to perform a real mapping to the perspPoints[] area
              QPointF topLeft = QPointF(0,0);
              QPointF topRight = QPointF(1000,0);
              QPointF bottomRight = QPointF(1000,1000);
              QPointF bottomLeft = QPointF(0,1000);
              float width = topRight.x() - topLeft.x();
              float height = bottomLeft.y() - topLeft.y();
      
              // Setup Projective trasform object
              genImageProjective imageProjective;
              imageProjective.sourceArea[0] = topLeft;
              imageProjective.sourceArea[1] = topRight;
              imageProjective.sourceArea[2] = bottomRight;
              imageProjective.sourceArea[3] = bottomLeft;
              imageProjective.destArea[0] = perspPoints[0];
              imageProjective.destArea[1] = perspPoints[1];
              imageProjective.destArea[2] = perspPoints[2];
              imageProjective.destArea[3] = perspPoints[3];
              // Compute projective transform coefficients
              if (imageProjective.computeCoeefficients() != 0)
                  return; // This can actually fail if any 3 points of Source or Dest are colinear
      
              // Initialize Grid parameters (without transform)
              float gridFirstLine = 0.1f; // The normalized position of first Grid Line (0.0 to 1.0)
              float gridStep = 0.1f;      // The normalized Grd size (=distance between grid lines: 0.0 to 1.0)
      
              // Draw Horizonal Grid lines
              QPointF lineStart, lineEnd, tempPnt;
              for (float pos = gridFirstLine; pos <= 1.0f; pos += gridStep)
              {
                  // Compute Grid Line Start
                  tempPnt = QPointF(topLeft.x(), topLeft.y() + pos*width);
                  imageProjective.mapSourceToDestPoint(tempPnt, lineStart);
                  // Compute Grid Line End
                  tempPnt = QPointF(topRight.x(), topLeft.y() + pos*width);
                  imageProjective.mapSourceToDestPoint(tempPnt, lineEnd);
      
                  // Draw Horizontal Line (use your prefered method to draw the line)
                  (...)
              }
              // Draw Vertical Grid lines
              for (float pos = gridFirstLine; pos <= 1.0f; pos += gridStep)
              {
                  // Compute Grid Line Start
                  tempPnt = QPointF(topLeft.x() + pos*height, topLeft.y());
                  imageProjective.mapSourceToDestPoint(tempPnt, lineStart);
                  // Compute Grid Line End
                  tempPnt = QPointF(topLeft.x() + pos*height, bottomLeft.y());
                  imageProjective.mapSourceToDestPoint(tempPnt, lineEnd);
      
                  // Draw Vertical Line (use your prefered method to draw the line)
                  (...)
              }
      (...)
      }
      
      ==========================================
      
      
      
      //========================================
      //C++ Header File: genImageProjective.h
      //========================================
      
      #ifndef GENIMAGE_H
      #define GENIMAGE_H
      
      #include <QPointF>
      
      // Class to transform an Image Point using Perspective transformation
      class genImageProjective
      {
      public:
          genImageProjective();
      
          int computeCoeefficients(void);
          int mapSourceToDestPoint(QPointF& sourcePoint, QPointF& destPoint);
      
      public:
          QPointF sourceArea[4]; // Source Image area limits (Rectangular)
          QPointF destArea[4];   // Destination Image area limits (Perspectivelly Transformed)
      
      private:
          static int gaussjordan(float  **a, float  *b, int n);
      
          bool coefficientsComputed;
          float vc[8];           // Vector of Transform Coefficients
      };
      
      #endif // GENIMAGE_H
      //========================================
      
      
      //========================================
      //C++ CPP File: genImageProjective.cpp
      //========================================
      
      #include <math.h>
      #include "genImageProjective.h"
      
      // ----------------------------------------------------
      // class genImageProjective
      // ----------------------------------------------------
      genImageProjective::genImageProjective()
      {
          sourceArea[0] = sourceArea[1] = sourceArea[2] = sourceArea[3] = QPointF(0,0);
          destArea[0] = destArea[1] = destArea[2] = destArea[3] = QPointF(0,0);
          coefficientsComputed = false;
      }
      
      
      // --------------------------------------------------------------
      // Compute projective transform coeeeficients
      // RetValue: 0: Success, !=0: Error
      /*-------------------------------------------------------------*
       *                Projective coordinate transformation         *
       *-------------------------------------------------------------*/
      /*!
       *  computeCoeefficients()
       *
       *      Input:  this->sourceArea[4]: (source 4 points; unprimed)
       *              this->destArea[4]:   (transformed 4 points; primed)
       *              this->vc  (computed vector of transform coefficients)
       *      Return: 0 if OK; <0 on error
       *
       *  We have a set of 8 equations, describing the projective
       *  transformation that takes 4 points (sourceArea) into 4 other
       *  points (destArea).  These equations are:
       *
       *          x1' = (c[0]*x1 + c[1]*y1 + c[2]) / (c[6]*x1 + c[7]*y1 + 1)
       *          y1' = (c[3]*x1 + c[4]*y1 + c[5]) / (c[6]*x1 + c[7]*y1 + 1)
       *          x2' = (c[0]*x2 + c[1]*y2 + c[2]) / (c[6]*x2 + c[7]*y2 + 1)
       *          y2' = (c[3]*x2 + c[4]*y2 + c[5]) / (c[6]*x2 + c[7]*y2 + 1)
       *          x3' = (c[0]*x3 + c[1]*y3 + c[2]) / (c[6]*x3 + c[7]*y3 + 1)
       *          y3' = (c[3]*x3 + c[4]*y3 + c[5]) / (c[6]*x3 + c[7]*y3 + 1)
       *          x4' = (c[0]*x4 + c[1]*y4 + c[2]) / (c[6]*x4 + c[7]*y4 + 1)
       *          y4' = (c[3]*x4 + c[4]*y4 + c[5]) / (c[6]*x4 + c[7]*y4 + 1)
       *
       *  Multiplying both sides of each eqn by the denominator, we get
       *
       *           AC = B
       *
       *  where B and C are column vectors
       *
       *         B = [ x1' y1' x2' y2' x3' y3' x4' y4' ]
       *         C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ]
       *
       *  and A is the 8x8 matrix
       *
       *             x1   y1     1     0   0    0   -x1*x1'  -y1*x1'
       *              0    0     0    x1   y1   1   -x1*y1'  -y1*y1'
       *             x2   y2     1     0   0    0   -x2*x2'  -y2*x2'
       *              0    0     0    x2   y2   1   -x2*y2'  -y2*y2'
       *             x3   y3     1     0   0    0   -x3*x3'  -y3*x3'
       *              0    0     0    x3   y3   1   -x3*y3'  -y3*y3'
       *             x4   y4     1     0   0    0   -x4*x4'  -y4*x4'
       *              0    0     0    x4   y4   1   -x4*y4'  -y4*y4'
       *
       *  These eight equations are solved here for the coefficients C.
       *
       *  These eight coefficients can then be used to find the mapping
       *  (x,y) --> (x',y'):
       *
       *           x' = (c[0]x + c[1]y + c[2]) / (c[6]x + c[7]y + 1)
       *           y' = (c[3]x + c[4]y + c[5]) / (c[6]x + c[7]y + 1)
       *
       */
      int genImageProjective::computeCoeefficients(void)
      {
          int retValue = 0;
          int     i;
          float  *a[8];  /* 8x8 matrix A  */
          float  *b = this->vc; /* rhs vector of primed coords X'; coeffs returned in vc[] */
      
          b[0] = destArea[0].x();
          b[1] = destArea[0].y();
          b[2] = destArea[1].x();
          b[3] = destArea[1].y();
          b[4] = destArea[2].x();
          b[5] = destArea[2].y();
          b[6] = destArea[3].x();
          b[7] = destArea[3].y();
      
          for (i = 0; i < 8; i++)
              a[i] = NULL;
          for (i = 0; i < 8; i++)
          {
              if ((a[i] = (float *)calloc(8, sizeof(float))) == NULL)
              {
                  retValue = -100; // ERROR_INT("a[i] not made", procName, 1);
                  goto Terminate;
              }
          }
      
          a[0][0] = sourceArea[0].x();
          a[0][1] = sourceArea[0].y();
          a[0][2] = 1.;
          a[0][6] = -sourceArea[0].x() * b[0];
          a[0][7] = -sourceArea[0].y() * b[0];
          a[1][3] = sourceArea[0].x();
          a[1][4] = sourceArea[0].y();
          a[1][5] = 1;
          a[1][6] = -sourceArea[0].x() * b[1];
          a[1][7] = -sourceArea[0].y() * b[1];
          a[2][0] = sourceArea[1].x();
          a[2][1] = sourceArea[1].y();
          a[2][2] = 1.;
          a[2][6] = -sourceArea[1].x() * b[2];
          a[2][7] = -sourceArea[1].y() * b[2];
          a[3][3] = sourceArea[1].x();
          a[3][4] = sourceArea[1].y();
          a[3][5] = 1;
          a[3][6] = -sourceArea[1].x() * b[3];
          a[3][7] = -sourceArea[1].y() * b[3];
          a[4][0] = sourceArea[2].x();
          a[4][1] = sourceArea[2].y();
          a[4][2] = 1.;
          a[4][6] = -sourceArea[2].x() * b[4];
          a[4][7] = -sourceArea[2].y() * b[4];
          a[5][3] = sourceArea[2].x();
          a[5][4] = sourceArea[2].y();
          a[5][5] = 1;
          a[5][6] = -sourceArea[2].x() * b[5];
          a[5][7] = -sourceArea[2].y() * b[5];
          a[6][0] = sourceArea[3].x();
          a[6][1] = sourceArea[3].y();
          a[6][2] = 1.;
          a[6][6] = -sourceArea[3].x() * b[6];
          a[6][7] = -sourceArea[3].y() * b[6];
          a[7][3] = sourceArea[3].x();
          a[7][4] = sourceArea[3].y();
          a[7][5] = 1;
          a[7][6] = -sourceArea[3].x() * b[7];
          a[7][7] = -sourceArea[3].y() * b[7];
      
          retValue = gaussjordan(a, b, 8);
      
      Terminate:
          // Clean up
          for (i = 0; i < 8; i++)
          {
              if (a[i])
                  free(a[i]);
          }
      
          this->coefficientsComputed = (retValue == 0);
          return retValue;
      }
      
      
      /*-------------------------------------------------------------*
       *               Gauss-jordan linear equation solver           *
       *-------------------------------------------------------------*/
      /*
       *  gaussjordan()
       *
       *      Input:   a  (n x n matrix)
       *               b  (rhs column vector)
       *               n  (dimension)
       *      Return:  0 if ok, 1 on error
       *
       *      Note side effects:
       *            (1) the matrix a is transformed to its inverse
       *            (2) the vector b is transformed to the solution X to the
       *                linear equation AX = B
       *
       *      Adapted from "Numerical Recipes in C, Second Edition", 1992
       *      pp. 36-41 (gauss-jordan elimination)
       */
      #define  SWAP(a,b)   {temp = (a); (a) = (b); (b) = temp;}
      int genImageProjective::gaussjordan(float  **a, float  *b, int n)
      {
          int retValue = 0;
          int i, icol=0, irow=0, j, k, l, ll;
          int *indexc = NULL, *indexr = NULL, *ipiv = NULL;
          float  big, dum, pivinv, temp;
      
          if (!a)
          {
              retValue = -1; // ERROR_INT("a not defined", procName, 1);
              goto Terminate;
          }
          if (!b)
          {
              retValue = -2; // ERROR_INT("b not defined", procName, 1);
              goto Terminate;
          }
      
          if ((indexc = (int *)calloc(n, sizeof(int))) == NULL)
          {
              retValue = -3; // ERROR_INT("indexc not made", procName, 1);
              goto Terminate;
          }
          if ((indexr = (int *)calloc(n, sizeof(int))) == NULL)
          {
              retValue = -4; // ERROR_INT("indexr not made", procName, 1);
              goto Terminate;
          }
          if ((ipiv = (int *)calloc(n, sizeof(int))) == NULL)
          {
              retValue = -5; // ERROR_INT("ipiv not made", procName, 1);
              goto Terminate;
          }
      
          for (i = 0; i < n; i++)
          {
              big = 0.0;
              for (j = 0; j < n; j++)
              {
                  if (ipiv[j] != 1)
                  {
                      for (k = 0; k < n; k++)
                      {
                          if (ipiv[k] == 0)
                          {
                              if (fabs(a[j][k]) >= big)
                              {
                                  big = fabs(a[j][k]);
                                  irow = j;
                                  icol = k;
                              }
                          }
                          else if (ipiv[k] > 1)
                          {
                              retValue = -6; // ERROR_INT("singular matrix", procName, 1);
                              goto Terminate;
                          }
                      }
                  }
              }
              ++(ipiv[icol]);
      
              if (irow != icol)
              {
                  for (l = 0; l < n; l++)
                      SWAP(a[irow][l], a[icol][l]);
                  SWAP(b[irow], b[icol]);
              }
      
              indexr[i] = irow;
              indexc[i] = icol;
              if (a[icol][icol] == 0.0)
              {
                  retValue = -7; // ERROR_INT("singular matrix", procName, 1);
                  goto Terminate;
              }
              pivinv = 1.0 / a[icol][icol];
              a[icol][icol] = 1.0;
              for (l = 0; l < n; l++)
                  a[icol][l] *= pivinv;
              b[icol] *= pivinv;
      
              for (ll = 0; ll < n; ll++)
              {
                  if (ll != icol)
                  {
                      dum = a[ll][icol];
                      a[ll][icol] = 0.0;
                      for (l = 0; l < n; l++)
                          a[ll][l] -= a[icol][l] * dum;
                      b[ll] -= b[icol] * dum;
                  }
              }
          }
      
          for (l = n - 1; l >= 0; l--)
          {
              if (indexr[l] != indexc[l])
              {
                  for (k = 0; k < n; k++)
                      SWAP(a[k][indexr[l]], a[k][indexc[l]]);
              }
          }
      
      Terminate:
          if (indexr)
              free(indexr);
          if (indexc)
              free(indexc);
          if (ipiv)
              free(ipiv);
          return retValue;
      }
      
      
      // --------------------------------------------------------------
      // Map a source point to destination using projective transform
      // --------------------------------------------------------------
      // Params:
      //  sourcePoint: initial point
      //  destPoint:   transformed point
      // RetValue: 0: Success, !=0: Error
      // --------------------------------------------------------------
      //  Notes:
      //   1. You must call once computeCoeefficients() to compute
      //      the this->vc[] vector of 8 coefficients, before you call
      //      mapSourceToDestPoint().
      //   2. If there was an error or the 8 coefficients were not computed,
      //      a -1 is returned and destPoint is just set to sourcePoint value.
      // --------------------------------------------------------------
      int genImageProjective::mapSourceToDestPoint(QPointF& sourcePoint, QPointF& destPoint)
      {
          if (coefficientsComputed)
          {
              float factor = 1.0f / (vc[6] * sourcePoint.x() + vc[7] * sourcePoint.y() + 1.);
              destPoint.setX( factor * (vc[0] * sourcePoint.x() + vc[1] * sourcePoint.y() + vc[2]) );
              destPoint.setY( factor * (vc[3] * sourcePoint.x() + vc[4] * sourcePoint.y() + vc[5]) );
              return 0;
          }
          else // There was an error while computing coefficients
          {
              destPoint = sourcePoint; // just copy the source to destination...
              return -1;               // ...and return an error
          }
      }
      //========================================
      

      【讨论】:

      • 您似乎在描述射影变换或单应性。例如,OpenCV 库(C++、Java、Python)具有估计这些变换并应用它们的函数。
      • @Rui:感谢您提供的信息。我实际上是在考虑在未来的某个时候使用和试验 OpenCV,所以这是一个额外的动机。
      • 因此,当没有两条边共线时,此解决方案有效。当每条边与另一条边共线时,我可以找到仿射变换。但是如果一对边是共线的而另一对不是共线的呢?我应该如何渲染它?
      • @IvanKuckir 我上面提出的解决方案在您描述的情况下工作正常。即使两个边对都是共线的(即在正常的矩形情况下),它也可以工作,但在后一种情况下变成线性变形;)(PS:我假设“共线”是指平行。如果您实际上是指共线,请提供一幅画,因为这超出了我的想象)
      • @FivosVilanakis 非常抱歉,我在自己的 gaussjordan() 实现中出现错误,当两条边平行时失败......我修复了它,它现在可以工作了 :) 顺便说一句。我正在使用它来转换照片,用户可以单独拖动图片的每个角落:) i.imgur.com/tCDfhZx.png
      【解决方案6】:

      最优雅和最快的解决方案是找到单应矩阵,它将矩形坐标映射到照片坐标。

      只要你知道你的数学,有了一个像样的矩阵库,这应该不是一项艰巨的任务。

      关键词:准线、单应性、直接线性变换

      但是,上面的递归算法应该可以工作,但如果您的资源有限,投影几何可能是唯一可行的方法。

      【讨论】:

        【解决方案7】:

        在特殊情况下,当您垂直于边 1 和 3 时,您可以将这些边分成相等的部分。然后画一条对角线,通过对角线和前面画的分界线的每个交点,画出与边1的平行线。

        【讨论】:

          【解决方案8】:

          使用 Breton 的细分方法(与 Mongo 的扩展方法有关),将获得准确的任意二次方除法。要使用这些方法分割成非二次幂分割,您必须细分到子像素间距,这在计算上可能会很昂贵。

          但是,我相信您可以将Haga's Theorem 的变体(在折纸中用于将一边划分为第 N 个给定的边划分为第 (N-1) 个)应用于透视正方形细分从最接近的 2 的幂产生任意除法,而不必继续细分。

          【讨论】:

          • Haga 的定理是一本非常有趣的读物,但它只适用于正方形。由于我的矩形的 h/w 比率可变,我似乎无法找到应用该理论的方法。
          • 我想我打算对每个细分进行“二分搜索”,而不是去亚像素精度。因此,如果我需要将我的矩形分成三部分,我基本上会在一定的精度范围内进行二分搜索,达到 33.33% 和 66.66%,然后使用每个“部分”进行加权二等分(我的意思是,如果我平分了 4 次,这相当于透视正确 16)
          【解决方案9】:

          您需要做的是在 3D(世界)中表示它,然后将其投影到 2D(屏幕)。

          这将要求您使用 4D 变换矩阵,该矩阵将 4D 齐次投影到 3D 齐次向量,然后您可以将其向下转换为 2D 屏幕空间向量。

          我在谷歌上也找不到,但是一本好的计算机绘图书会有详细信息。

          关键词是投影矩阵、投影变换、仿射变换、齐次向量、世界空间、屏幕空间、透视变换、3D变换

          顺便说一句,这通常需要几节课来解释所有这些。祝你好运。

          【讨论】:

          • 使用 3D 数学来完成这项工作是大材小用,计算复杂度远远超出所需。
          • 只是一堆数学运算,不需要任何 IO。所以它会超级快。
          • “超快”是相对的。不使用 3D 数学将是“超快”ER。将数百万个处理器周期投入到一个应该花费数千个的问题上只是自找麻烦。当他决定使用 1024x1024 网格而不是 5x5 时会发生什么?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-09
          • 1970-01-01
          相关资源
          最近更新 更多