【问题标题】:Perspective Projection effect correction透视投影效果校正
【发布时间】:2015-08-17 22:34:30
【问题描述】:

我试图从上述 3D 球体的 8 个顶点在 3D 空间中绘制 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

我期望输出如下:

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

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

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

【问题讨论】:

  • 您要投影到哪个平面上?我觉得你的一些代码在这里丢失了。
  • XY 平面。我在这里使代码简洁。你需要看哪些细节?
  • 您的转换矩阵丢失。你把你的观点放在原点。根据您的预期输出,我希望视点位于x=60
  • @beaker,好的。该怎么做?
  • @Dan,是的。其实,我叫错了。我需要知道的主要事情是,如何移动我的相机。

标签: projection


【解决方案1】:

我从 cmets 中看到,您通过一个巧妙的公式实现了您想要的结果。如果您对使用矩阵的“标准”图形方式感兴趣,希望这篇文章对您有所帮助。

我找到了一个解释 OpenGL 投影矩阵的优秀页面,它也延伸到投影的一般数学。

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

下图显示了您尝试做的第一部分。

所以左边的图像是您希望相机看到的“观看量”。您可以看到,在这种情况下,投影中心(基本上是相机的焦点)位于原点。

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

我们在这里所做的是获取左侧形状奇特的体积,并将其转换为右侧的我们所说的“归一化坐标”。因此,我们将每个方向的观看量映射到 -1 到 1 的范围内。基本上,我们在数学上将不规则形状的观察体拉伸到这个以原点为中心的 2x2x2 立方体中。

这个操作是通过下面的矩阵完成的,同样来自我上面链接的优秀文章。

所以请注意,您有六个变量。

  • t = 顶部

  • b = 底部

  • l = 左

  • r = 正确

  • n = 附近

  • f = 远

这六个变量定义了您的观看量。 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 的 2D 图像。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-06
    • 2013-11-26
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多