透视投影效果校正

时间:2015-08-17 22:34:30

标签: projection

enter image description here

我试图从上面3D sphare的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

我期待输出如下: enter image description here

但输出结果如下: enter image description here

我真的很想移动我的观察镜头。

如何达到我想要的效果?

1 个答案:

答案 0 :(得分:3)

我从评论中看到,您通过巧妙的公式获得了理想的结果。如果你对使用矩阵的“标准”图形方式感兴趣,我希望这篇文章可以帮助你。

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

如果你想深入探讨here is the very well written article,请详细解释它的步骤,并且总体上非常值得称道。

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

perspective projection visualized

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

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

我们在这里做的是将左侧奇怪形状的音量转换为我们称之为右侧的“标准化坐标”。因此,我们将观察量映射到每个方向的-1到1的范围。基本上,我们通过数学方式将不规则形状的观察体积拉伸到以原点为中心的2x2x2立方体中。

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

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

  • t = top

  • b = bottom

  • l = left

  • r = right

  • n = near

  • f =远

这六个变量定义了您查看音量。 Far未在上图中标注,但它是图像中距离原点最远的平面的距离。

perspective projection matrix

上图显示了将观看体积输出到标准化坐标的投影矩阵。一旦坐标处于这种形式,您可以通过简单地忽略z坐标使其平坦,这类似于您已经完成的一些工作(很好的工作!)。

所以我们都准备好从原点查看东西。但是,让我们说我们不希望从原点进行查看,而是希望从后面和后面的某个地方查看。

我们可以做到!但是不是移动我们的观察区域(我们在这里很好地计算了数学),它可能是直观的,更容易移动我们想要查看的所有点。

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

transformation matrix

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

你可能也注意到我展示的两个矩阵都是4x4,你的坐标是3x1。这是因为矩阵意味着与齐次坐标一起使用。这些看似奇怪,因为它们使用4个变量来表示3D点,但它只是x,y,z和w,在这里你可以为你的点做w = 1。我相信这个变量用于深度缓冲,除此之外,它基本上普遍存在于图形的矩阵数学中,所以你会习惯于使用它。

现在你有这些矩阵,你可以将翻译应用到你的点,然后将视角应用到你得到的那些点。然后简单地忽略z组件,那就是你!您在x和y方向上有一个从-1到1的2D图像。