透视投影效果校正

Perspective Projection effect correction

我试图从上述 3D 球体的 8 个顶点绘制 3D space 中的 8 个点。

我使用了以下代码:

#include "Coordinates2d.h"
#include "Point3d.h"

const double zoom = 500;

int main()
{
    Coordinates2d::ShowWindow("3D Primitives!");

    std::vector<Point3d> points;
    points.push_back(Point3d(0,0,20));
    points.push_back(Point3d(0,100,20));
    points.push_back(Point3d(120,100,20));
    points.push_back(Point3d(120,0,20));
    points.push_back(Point3d(0,0,120));
    points.push_back(Point3d(0,100,120));
    points.push_back(Point3d(120,100,120));
    points.push_back(Point3d(120,0,120));

    for(int i=0 ; i<points.size() ; i++)
    {
        Coordinates2d::Draw(points[i], zoom);
    }

    Coordinates2d::Wait();
}

其中,Point3D是这样的:

#ifndef _POINT_3D_
#define _POINT_3D_

#include "graphics.h"
#include "Matrix.h"
#include "Point2d.h"
#include <cmath>
#include <iostream>

struct Point3d
{
    double x;
    double y;
    double z;
public:
    Point3d();
    Point3d(double x, double y, double z);
    Point3d(Point3d const & point);
    Point3d & operator=(Point3d const & point);
    Point3d & operator+(int scalar);
    bool operator==(Point3d const & point);
    bool operator!=(Point3d const & point);
    Point3d Round()
    {
        return Point3d(floor(this->x + 0.5), floor(this->y + 0.5), floor(this->z + 0.5));
    }
    void Show()
    {
        std::cout<<"("<<x<<", "<<y<<", "<<z<<")";
    }
    bool IsValid();
    double Distance(Point3d & point);
    void SetMatrix(const Matrix & mat);
    Matrix GetMatrix() const;
    Point2d ConvertTo2d(double zoom)
    {
        return Point2d(x*zoom/(zoom-z), y*zoom/(zoom-z));
    }
};
#endif

#ifndef _COORDINATES_2D_
#define _COORDINATES_2D_

#include "graphics.h"
#include "Point2d.h"
#include "Point3d.h"
#include "Line3d.h"

class Coordinates2d
{
private:
    static Point2d origin;

public: 
    static void Wait();
    static void ShowWindow(char str[]);
private:
    static void Draw(Point2d & pt);
public:
    static void Draw(Point3d & pt, double zoom)
    {
        Coordinates2d::Draw(pt.ConvertTo2d(zoom));
    }
};

#endif

我期望输出如下:

但是输出变成了下面这样:

其实我有兴趣移动我的观察相机。

我怎样才能达到我想要的结果?

我从评论中看到您使用巧妙的公式达到了预期的结果。如果您有兴趣以 'standard' 图形方式使用矩阵,我希望这篇 post 对您有所帮助。

我发现了一个很好的页面,解释了 OpenGL 的投影矩阵,它还扩展到投影的一般数学。

如果你想深入,here is the very well written article,详细解释了它的步骤,总的来说值得称道。

下图显示了您要执行的操作的第一部分。

所以左边的图像是您希望相机看到的'viewing volume'。你可以看到在这种情况下,投影中心(基本上是相机的焦点)在原点。

但是等等,你说,我不希望投影中心位于原点!我知道,我们稍后再讲。

我们在这里所做的是将左侧形状奇怪的体积,并将其转换为我们称之为右侧的 'normalized coordinate'。所以我们在每个方向上将观看量映射到 -1 到 1 的范围内。基本上,我们用数学方法将不规则形状的观察体积拉伸到这个以原点为中心的 2x2x2 立方体中。

此操作是通过以下矩阵完成的,再次来自我上面链接的优秀文章。

请注意,您有六个变量。

  • t = top

  • b = 底部

  • l=左

  • r = 右

  • n = 附近

  • f=far

这六个变量定义了您的观看量。 Far在上图中没有标出,而是图中距离原点最远平面的距离

上图显示了将观看体积放入归一化坐标的投影矩阵。一旦坐标采用这种形式,您可以通过简单地忽略 z 坐标使其变平,这类似于您所做的一些工作(干得好!)。

所以我们都准备好从源头查看事物了。但是假设我们不想从原点查看,而更愿意从后面和侧面的某个地方查看。

好吧,我们可以做到!但不是移动我们的查看区域(我们在这里已经很好地计算出数学),它可能违反直觉,更容易移动我们试图查看的所有点。

这可以通过将所有点乘以平移矩阵来完成。 这是the wikipedia page for translation,我从中得到了以下矩阵。

Vx、Vy 和 Vz 是我们想要在 x、y 和 z 方向上移动物体的量。请记住,如果我们想在正 x 方向移动相机,我们需要负 Vx,反之亦然。这是因为我们移动的是点而不是相机。如果你愿意,请随意尝试看看。

你可能也注意到了,我展示的两个矩阵都是4x4的,而你的坐标是3x1的。这是因为矩阵要与 齐次坐标 一起使用。这些看起来很奇怪,因为它们使用 4 个变量来表示一个 3D 点,但它只是 x、y、z 和 w,您让 w = 1 来表示您的点。我相信这个变量用于深度缓冲区等,但它基本上普遍存在于图形的矩阵数学中,因此您需要习惯使用它。

现在你有了这些矩阵,你可以将平移矩阵应用到你的点上,然后将透视矩阵应用到你得到的那些点上。然后简单地忽略 z 组件,就可以了!您在 x 和 y 方向上有一个从 -1 到 1 的二维图像。