Search code examples
projection

Perspective Projection effect correction


enter image description here

I was trying to plot 8 points in a 3D space from the 8 vertices of the above 3D sphare.

I used the following code:

#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();
}

Where, the Point3D is like the following:

#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

I was expecting the output to be the following: enter image description here

But the output became like the following: enter image description here

I am actually interested to move my viewing camera.

How can I achieve my desired result?


Solution

  • I see from the comments that you achieved your desired result with a clever formula. If you're interested in doing it the 'standard' graphics way, using matrices, I hope this post will help you.

    I found an excellent page written explaining projection matrices for OpenGL, which also extends to the general mathematics of projection.

    If you want to go in depth, here is the very well written article, explains it's steps in detail, and is just overall highly commendable.

    The below image shows the first part of what you're trying to do.

    perspective projection visualized

    So the image on the left is the 'viewing volume' that you want your camera to see. You can see that in this case, the Center of Projection (basically the focal point of the camera) is at the origin.

    But wait, you say, I don't WANT the center of projection to be at the origin! I know, we'll cover that later.

    What we're doing here is taking the strangely shaped volume on the left, and converting it to what we call 'normalized coordinate' on the right. So we're mapping out viewing volume onto the range of -1 to 1 in each direction. Basically, we mathmatically stretch the irregularly shaped viewing volume into this 2x2x2 cube centered at the origin.

    This operation is accomplished through the following matrix, again, from the excellent article I linked above.

    So note you have six variables.

    • t = top

    • b = bottom

    • l = left

    • r = right

    • n = near

    • f = far

    Those six variables define you viewing volume. Far is not labeled on the above image, but it is the distance of the furthest plane from the origin in the image.

    perspective projection matrix

    The above image shows the projection matrix that puts out viewing volume into normalized coordinates. Once coordinates are in this form, you can make it flat by simply ignoring the z coordinate, which is similar to some of the work you have done (nice work!).

    So we're all set with that for viewing things from the origin. But let's say we don't want to view from the origin, and would prefer to view from, say somewhere behind and to the side.

    Well we can do that! but instead of moving our viewing area (we have the math all nicely worked out right here), it is perhaps counter intuitively, easier to move all the points we are trying to view.

    This can be done by multiplying all of the points by a translation matrix. Here is the wikipedia page for translation, from which I took the following matrix.

    transformation matrix

    Vx, Vy, and Vz are the amount we want to move things in the x, y, and z directions. Keep in mind, if we want to move the camera in the positive x direction, we need a negative Vx, and vice versa. This is because we are moving the points instead of the camera. Feel free to try it and see, if you want.

    You may also have noticed that both of the matrices I showed are 4x4, and your coordinates are 3x1. This is because the matrices are meant to be used with homogeneous coordinates. These seem strange because they use 4 variables to represent a 3D point, but its just x, y, z, and w, where you make w =1 for your points. I believe this variable is used for depth buffers, among other things, but it is basically ubiquitously present in graphics' matrix math, so you'll want to get used to using it.

    Now that you have these matrices, you can apply the translation one to your points, then apply the perspective one to those points you got out. Then simply ignore the z components, and there you are! You have a 2D image from -1 to 1 in the x and y directions.