将三维物体投影到二维平面需要考虑投影变换,分为三步,即照相机变换,投影变换和视口变换。
照相机变换:考虑到照相机和物体相对静止,故将照相机规范化为(1)放在坐标原点 (2)看向负z轴 (3)向上的方向为Y正向。此过程中物体做相同运动,则看到的效果与变换前一致。
投影变换:分为正交投影和透视投影,此处具体算法的实现主要参考闫令琪老师的课程GAMES101-现代计算机图形学入门-闫令琪
视口变换:将物体规范化投影到屏幕上。
类的设计如下:(此处需要用到CVector3类,即向量类的相关运算)
#pragma once
#include "P3.h"
#include "Vector3.h"
#include "ModelTransform3.h"
class CViewingTranformation
{
public:
CViewingTranformation(void);
virtual ~CViewingTranformation(void);
void SetCamera(CP3 p);//设置照相机的位置(可以是直角坐标,也可以是球坐标)
void ChangeCameraPos(int dx, int dy, int dz);//改变照相机位置
void ChangeLookAtDir(int theta);//改变照相机看向的方向
void ChangeUpDirection(int theta);//改变照相机看向的方向
CP3 GetCamera(void);//读取视点照相机的信息;
CP3 RviewTview(CP3 WorldPoint);//照相机变到原点
CP3 TransportTrans(CP3 ViewPoint,int scale);//将图移到原点进行规范化
CVector3 GetLookAtDir(void);//读取照相机看向的方向
CP3 OrthogonalProjection(CP3 WorldPoint);//正交投影 :计算三维点投影后的点坐标
CP3 PerspectiveProjection(CP3 WorldPoint);//透视投影:计算三维点投影后的点坐标
public:
//照相机的描述;
CP3 EyePoint;//照相机的位置;
CVector3 LookAtDir;//照相机看向的方向;
CVector3 UpDirection;//照相机向上的方向;
CModelTransform3 tran;
//正交投影视上下左右面
double l, r, t, b;//长方体左右上下面的位置
//视景锥的描述:
double fovY;
double aspect;//宽纵比(可以看成是映射成的长方体的近平面的宽高比)
double n, f;//近平面,原平面
};
具体代码实现如下:
#include "pch.h"
#include "ViewingTranformation.h"
#define PI 3.1415926
CViewingTranformation::CViewingTranformation(void) {
EyePoint.x = 0;EyePoint.y = 0;EyePoint.z = 15;
LookAtDir = CVector3(0, 0, -1);
UpDirection = CVector3(0, 1, 0);;
f = -1000;n = -6;
fovY = 90 * PI / 180;
aspect = 3;
t = tan(fovY / 2) * -n; b = -t;
r = aspect * t; l = -r;
}
CViewingTranformation::~CViewingTranformation(void) {
}
void CViewingTranformation::ChangeCameraPos(int dx, int dy, int dz) {
EyePoint.x += dx;
EyePoint.y += dy;
EyePoint.z += dz;
}
void CViewingTranformation::ChangeLookAtDir(int theta) {
double the = theta * PI / 180;
double Newx = cos(the) * LookAtDir.x + sin(the) * LookAtDir.z;
double Newz = -sin(the) * LookAtDir.x + cos(the) * LookAtDir.z;
LookAtDir.x = Newx;
LookAtDir.z = Newz;
}
void CViewingTranformation::ChangeUpDirection(int theta) {
double the = theta * PI / 180;
double Newx = cos(the) * UpDirection.z - sin(the) * UpDirection.y;
double Newz = sin(the) * UpDirection.z + cos(the) * UpDirection.y;
UpDirection.z = Newx;
UpDirection.y = Newz;
}
void CViewingTranformation::SetCamera(CP3 p) {
this->EyePoint = p;
}
CVector3 CViewingTranformation::GetLookAtDir(void)
{
// TODO: 在此处添加实现代码.
return this->LookAtDir;
}
CP3 CViewingTranformation::GetCamera(void) {
CP3 Point;
Point = EyePoint;
return Point;
}
CP3 CViewingTranformation::RviewTview(CP3 WorldPoint)
{
// TODO: 在此处添加实现代码.
WorldPoint.x = WorldPoint.x - EyePoint.x * WorldPoint.w;
WorldPoint.y = WorldPoint.y - EyePoint.y * WorldPoint.w;
WorldPoint.z = WorldPoint.z - EyePoint.z * WorldPoint.w;
CP3 ViewPoint;
double M[4][4];
CVector3 u, v, w;
u = CrossProduct(LookAtDir, UpDirection);
u.Normalize();
w = UpDirection.Normalize();
v = CrossProduct(w, u);
M[0][0] = u.x;M[0][1] = u.y;
M[0][2] = u.z;M[0][3] = 0;
M[1][0] = w.x;M[1][1] = w.y;M[1][2] = w.z;M[1][3] = 0;
M[2][0] = -v.x;M[2][1] = -v.y;M[2][2] = -v.z;M[2][3] = 0;
M[3][0] = 0;M[3][1] = 0;M[3][2] = 0;M[3][3] = 1;
M[0][0] = LookAtDir.y * UpDirection.z - LookAtDir.z * UpDirection.y;M[0][1] = LookAtDir.z * UpDirection.x - LookAtDir.x * UpDirection.z;
M[0][2] = LookAtDir.x * UpDirection.y - LookAtDir.y * UpDirection.x;M[0][3] = 0;
M[1][0] = UpDirection.x;M[1][1] = UpDirection.y;M[1][2] = UpDirection.z;M[1][3] = 0;
M[2][0] = -LookAtDir.x;M[2][1] = -LookAtDir.y;M[2][2] = -LookAtDir.z;M[2][3] = 0;
M[3][0] = 0;M[3][1] = 0;M[3][2] = 0;M[3][3] = 1;
ViewPoint.x = M[0][0] * WorldPoint.x + M[0][1] * WorldPoint.y + M[0][2] * WorldPoint.z + M[0][3] * WorldPoint.w;
ViewPoint.y = M[1][0] * WorldPoint.x + M[1][1] * WorldPoint.y + M[1][2] * WorldPoint.z + M[1][3] * WorldPoint.w;
ViewPoint.z = M[2][0] * WorldPoint.x + M[2][1] * WorldPoint.y + M[2][2] * WorldPoint.z + M[2][3] * WorldPoint.w;
ViewPoint.w = M[3][0] * WorldPoint.x + M[3][1] * WorldPoint.y + M[3][2] * WorldPoint.z + M[3][3] * WorldPoint.w;
ViewPoint.c = WorldPoint.c;
return ViewPoint;
}
CP3 CViewingTranformation::OrthogonalProjection(CP3 WorldPoint) {
WorldPoint.x = 2 * (WorldPoint.x - (-1.0) * (r + l) * WorldPoint.w / 2) / (r - l);
WorldPoint.y = 2 * (WorldPoint.y - (-1.0) * (t + b) * WorldPoint.w / 2) / (t - b);
WorldPoint.z = 2 * (WorldPoint.z - (-1.0) * (n + f) * WorldPoint.w / 2) / (n - f);
//WorldPoint = TransportTrans(WorldPoint);
return WorldPoint;
}
CP3 CViewingTranformation::PerspectiveProjection(CP3 WorldPoint) {
CP3 ViewPoint;
ViewPoint.x = n * WorldPoint.x;
ViewPoint.y = n * WorldPoint.y;
ViewPoint.z = (n + f) * WorldPoint.z - (1.0) * n * f * WorldPoint.w;
ViewPoint.w = WorldPoint.z;
ViewPoint.c = WorldPoint.c;
CP3 ScreenPoint = OrthogonalProjection(ViewPoint);
ScreenPoint.x = ScreenPoint.x / ScreenPoint.w;
ScreenPoint.y = ScreenPoint.y / ScreenPoint.w;
ScreenPoint.z = ScreenPoint.z / ScreenPoint.w;
ScreenPoint.w = ScreenPoint.w / ScreenPoint.w;
return ScreenPoint;
}
CP3 CViewingTranformation::TransportTrans(CP3 ViewPoint,int scale) {
CP3 ViewportPoint;
ViewportPoint.x = (r - l) / 2 * scale * ViewPoint.x;
ViewportPoint.y = (t - b) / 2 * scale * ViewPoint.y;
ViewportPoint.z = scale * ViewPoint.z;
ViewportPoint.w = ViewPoint.w;
ViewportPoint.c = ViewPoint.c;
return ViewportPoint;
}