投影

正交投影

对于物体上任意一点的三维坐标P(x,y,z),投影后的三维坐标为P′(x′,y′,z′)P^\prime(x^\prime,y^\prime,z^\prime)P′(x′,y′,z′),那么正交投影的方程为{x′=xy′=yz′=0\begin{cases} x^\prime=x\\y^\prime=y\\z^\prime=0 \end{cases}⎩⎨⎧​x′=xy′=yz′=0​

斜投影

在这里插入图片描述

如图所示,空间中一点P1(x,y,z)P_1(x,y,z)P1​(x,y,z)在xOy面上的斜投影坐标P2(x′,y′,0)P_2(x^\prime,y^\prime,0)P2​(x′,y′,0)正交投影点P3(x,y,0)P_3(x,y,0)P3​(x,y,0),那么有{x′=x−Lcos⁡β=x−zcot⁡αcos⁡βy′=y−Lsin⁡β=y−zcot⁡αsin⁡β\begin{cases} x^\prime=x-L\cos\beta=x-z\cot\alpha\cos\beta\\ y^\prime=y-L\sin\beta=y-z\cot\alpha\sin\beta \end{cases}{x′=x−Lcosβ=x−zcotαcosβy′=y−Lsinβ=y−zcotαsinβ​

透视投影

透视投影有三个坐标系,世界坐标系,观察坐标系,屏幕坐标系
在这里插入图片描述

在这里插入图片描述

观察坐标系到投影坐标系的转换可以用相似求出
在这里插入图片描述

{x′=n⋅xzy′=n⋅yz\begin{cases} x^\prime=n\cdot \frac{x}{z}\\ y^\prime=n\cdot \frac{y}{z} \end{cases}{x′=n⋅zx​y′=n⋅zy​​

建立投影类

class CProjection
{
public:
	CProjection();
	~CProjection();
	void SetViewPoint(double R);
	CPoint3 GetViewPoint();
	CPoint2 ObliqueProjection(CPoint3 WorldPoint);//正交投影
	CPoint2 OrthogonalProjection(CPoint3 WorldPoint);//斜二测投影
	CPoint2 PerspectiveProjection(CPoint3 WorldPoint);//透视投影
private:
	CPoint3 m_viewPoint;
	double R, d;
};

CProjection::CProjection()
{
    R = 1200; d = 800;
    m_viewPoint.m_x = 0;
    m_viewPoint.m_y = 0;
    m_viewPoint.m_z = R;
}

CProjection::~CProjection()
{
}

void CProjection::SetViewPoint(double R)
{
    this->R = R;
}

CPoint3 CProjection::GetViewPoint()
{
    return m_viewPoint;
}

CPoint2 CProjection::ObliqueProjection(CPoint3 WorldPoint)//斜二测投影
{
    CPoint2 ScreenPoint;
    ScreenPoint.m_x = WorldPoint.m_x - 0.3536 * WorldPoint.m_z;
    ScreenPoint.m_y = WorldPoint.m_y - 0.3536 * WorldPoint.m_z;
    return ScreenPoint;
}

CPoint2 CProjection::OrthogonalProjection(CPoint3 WorldPoint)//正交投影
{
    CPoint2 ScreenPoint;
    ScreenPoint.m_x = WorldPoint.m_x ;
    ScreenPoint.m_y = WorldPoint.m_y ;
    return ScreenPoint;
}

CPoint2 CProjection::PerspectiveProjection(CPoint3 WorldPoint)
{
    CPoint2 ScreenPoint;
    CPoint3 ViewPoint;
    ViewPoint.m_x = WorldPoint.m_x;
    ViewPoint.m_y = WorldPoint.m_y;
    ViewPoint.m_z = m_viewPoint.m_z - WorldPoint.m_z;
    ScreenPoint.m_x = d * ViewPoint.m_x / ViewPoint.m_z;
    ScreenPoint.m_y = d * ViewPoint.m_y / ViewPoint.m_z;
    return ScreenPoint;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

消隐,背面剔除算法

给定视点位置或视线方向后,确定场景中哪些物体表面是可见的、哪些物体表面是不可见的,即是消隐。
背面剔除算法主要是针对凸多面体,其表面要么可见,要么不可见。算法要给出测试每个表面是否可见的表达式。
在这里插入图片描述
如图所示,根据表面的外法向量N⃗\vec NN和式向量V⃗\vec VV的夹角θ\thetaθ来进行可见性判定。
N⃗=P2P4⃗×P3P4⃗\vec N=\vec {P_2P_4} \times \vec {P_3P_4}N=P2​P4​​×P3​P4​​
给定视点坐标O(xv,yv,zv)O(x_v,y_v,z_v)O(xv​,yv​,zv​)后,视向量表示为V⃗=(xv−x4,yv−y4,zv−z4)\vec V=(x_v-x_4,y_v-y_4,z_v-z_4)V=(xv​−x4​,yv​−y4​,zv​−z4​)
将法向量N⃗\vec NN归一化为单位向量n⃗\vec nn,将视向量归一化为单位向量v⃗\vec vv,则有
n⃗⋅v⃗=cos⁡θ\vec n \cdot \vec v=\cos\thetan⋅v=cosθ
可见性的判定如下:

  • 当cos⁡θ>0\cos\theta>0cosθ>0时,表面可见,绘制多边形的边界线
  • 当cos⁡θ=0\cos\theta=0cosθ=0时,表面多边形退化为一条直线
  • 当cos⁡θ<0\cos\theta<0cosθ<0时,表面多边形不可见
    为了实现背面剔除算法,这里设计一个三维向量类CVector3

这里给的例子是一个立方体,那如果是一个贝塞尔曲面拟合的物体(比如球),那么用递归曲面片的方式绘制曲面,对于每一个细分曲面片又可以看成一个平面四边形,求出法向量即可。
在这里插入图片描述

三维向量类CVector3

class CVector3
{
public:
	CVector3();
	virtual ~CVector3();
	CVector3(double x, double y, double z);//绝对向量
	CVector3(const CPoint3& p);
	CVector3(const CPoint3& p0, const CPoint3& p1);//相对向量
	double Magnitude();//计算向量的模
	CVector3 Normalize();//归一化向量
	friend CVector3 operator + (const CVector3& v0, const CVector3& v1);//运算符重载
	friend CVector3 operator - (const CVector3& v0, const CVector3& v1);
	friend CVector3 operator * (const CVector3& v, double t);
	friend CVector3 operator * (double t, const CVector3& v);
	friend CVector3 operator / (const CVector3& v, double t);
	friend double DotProduct(const CVector3& v0, const CVector3& v1);//计算向量的点积
	friend CVector3 CrossProduct(const CVector3& v0, const CVector3& v1);//计算向量的叉积
private:
	double m_x, m_y, m_z;
};
CVector3::CVector3(void)
{
	m_x = 0.0, m_y = 0.0, m_z = 1.0;//指向z轴正向
}


CVector3::~CVector3(void)
{
}

CVector3::CVector3(double x, double y, double z)//绝对向量
{
	m_x = x;
	m_y = y;
	m_z = z;
}
CVector3::CVector3(const CPoint3& p)
{
	m_x = p.m_x;
	m_y = p.m_y;
	m_z = p.m_z;
}
CVector3::CVector3(const CPoint3& p0, const CPoint3& p1)//相对向量
{
	m_x = p1.m_x - p0.m_x;
	m_y = p1.m_y - p0.m_y;
	m_z = p1.m_z - p0.m_z;
}
double CVector3::Magnitude(void)//向量的模
{
	return sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
}
CVector3 CVector3::Normalize(void)//归一化为单位向量
{
	CVector3 vector;
	double magnitude = sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
	if (fabs(magnitude) < 1e-6)
		magnitude = 1.0;
	vector.m_x = m_x / magnitude;
	vector.m_y = m_y / magnitude;
	vector.m_z = m_z / magnitude;
	return vector;
}
void CVector3::IntoOut()
{
	if (m_x * m_y < 0&&m_x*m_z>0) {
		m_x = -m_x, m_z = -m_z;
	}
	else if (m_x * m_z < 0 && m_x * m_y>0) {
		m_x = -m_x, m_y = -m_y;
	}
	else if (m_y * m_z < 0 && m_y * m_z>0) {
		m_y = -m_y, m_z = -m_z;
	}
}
CVector3 operator + (const CVector3& v0, const CVector3& v1)//向量的和
{
	CVector3 vector;
	vector.m_x = v0.m_x + v1.m_x;
	vector.m_y = v0.m_y + v1.m_y;
	vector.m_z = v0.m_z + v1.m_z;
	return vector;
}

CVector3 operator - (const CVector3& v0, const CVector3& v1)//向量的差
{
	CVector3 vector;
	vector.m_x = v0.m_x - v1.m_x;
	vector.m_y = v0.m_y - v1.m_y;
	vector.m_z = v0.m_z - v1.m_z;
	return vector;
}

CVector3 operator * (const CVector3& v, double t)//向量与常量的积
{
	CVector3 vector;
	vector.m_x = v.m_x * t;
	vector.m_y = v.m_y * t;
	vector.m_z = v.m_z * t;
	return vector;
}

CVector3 operator * (double t, const CVector3& v)//常量与向量的积
{
	CVector3 vector;
	vector.m_x = v.m_x * t;
	vector.m_y = v.m_y * t;
	vector.m_z = v.m_z * t;
	return vector;
}

CVector3 operator / (const CVector3& v, double scalar)//向量数除
{
	if (fabs(scalar) < 1e-6)
		scalar = 1.0;
	CVector3 vector;
	vector.m_x = v.m_x / scalar;
	vector.m_y = v.m_y / scalar;
	vector.m_z = v.m_z / scalar;
	return vector;
}

double DotProduct(const CVector3& v0, const CVector3& v1)//向量的点积
{
	return(v0.m_x * v1.m_x + v0.m_y * v1.m_y + v0.m_z * v1.m_z);
}

CVector3 CrossProduct(const CVector3& v0, const CVector3& v1)//向量的叉积
{
	CVector3 vector;
	vector.m_x = v0.m_y * v1.m_z - v0.m_z * v1.m_y;
	vector.m_y = v0.m_z * v1.m_x - v0.m_x * v1.m_z;
	vector.m_z = v0.m_x * v1.m_y - v0.m_y * v1.m_x;
	return vector;
}

示例,球的消隐

在这里插入图片描述

需要项目代码请评论区留言或私信

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐