实制作平面体或者二次曲面体的三维物体线框模型动画

本文详细描述了一个实验项目,涉及设计物体线框模型类、使用CTransform3进行三维变换以及与图形的交互,包括鼠标和键盘控制。通过实例展示了如何在三维场景中显示和操作金字塔模型,同时分享了实验过程中遇到的问题和学习体验。
摘要由CSDN通过智能技术生成

一、实验项目要求

1.设计物体线框模型类;

2.调用三维变换类将物体导入三维场景(CTgestView类)中显示;

3.使用鼠标或者键盘方向键,与图形进行交互。

二、理论分析或算法分析

1.设计物体线框模型类:

在这个实验中,设计一个金字塔线框模型,在Pyramid.h中定义一个面表(void CPyramid::ReadFacet(void)//面表)和点表(void CPyramid::ReadPoint(void)//点表),(void Draw(CDC* pDC);//绘制金字塔线框)来进行对线框模型的绘制;在绘制图形的时候采用正交投影的方式(ScreenPoint.x = P[F[nFacet].Index[nPoint]].x;,ScreenPoint.y = P[F[nFacet].Index[nPoint]].y;),绘制的时候采用一点到另一个点的绘制方式(pDC->MoveTo(ROUND(ScreenPoint.x),ROUND(ScreenPoint.y));pDC->LineTo(ROUND(ScreenPoint.x), ROUND(ScreenPoint.y));)最后闭合多边形(pDC->LineTo(ROUND(temp.x), ROUND(temp.y));//闭合多边形);

2.使用三维变换类CTransform3类来进行三维的变换,在这个类中定义了三维顶点数组初始化(void CTransform3::SetMatrix(CP3* P, int ptNumber)//顶点数组初始化)和单位矩阵初始化(void CTransform3::Identity(void)//单位矩阵初始化)和平移变换(void CTransform3::Translate(double tx,double ty,double tz)//平移变换)和相对于任意点的比例变换(void CTransform3::Scale(double sx, double sy, double sz, CP3 p)//相对于任意点的缩放变换)和绕X轴旋转变换(void CTransform3::RotateX(double beta)//绕X轴旋转变换)和绕Y轴旋转变换(void CTransform3::RotateY(double beta)//绕Y轴旋转变换)和绕Z轴旋转变换(void CTransform3::RotateZ(double beta)//绕Z轴旋转变换)和相对于任意点的绕X轴旋转变换(void CTransform3::RotateX(double beta, CP3 p)//相对于任意点的X轴旋转变换)和相对于任意点的绕Y轴旋转变换(void CTransform3::RotateY(double beta, CP3 p)//相对于任意点的Y轴旋转变换)和相对于任意点的绕Z轴旋转变换(void CTransform3::RotateZ(double beta, CP3 p)//相对于任意点的Z轴旋转变换)和关于X轴反射变换(void CTransform3::ReflectX(void)//X轴反射变换)和关于Y轴反射变换(void CTransform3::ReflectY(void)//Y轴反射变换)和关于Z轴反射变换(void CTransform3::ReflectZ(void)//Z轴反射变换)和关于XOY面反射变换(void CTransform3::ReflectXOY(void)//XOY面反射变换)和关于YOZ面反射变换(void CTransform3::ReflectYOZ(void)//YOZ面反射变换)和关于ZOX面反射变换(void CTransform3::ReflectZOX(void)//XOZ面反射变换)和沿X方向错切变换(void CTransform3::ShearX(double b, double c)//X方向错切变换)和沿Y方向错切变换(void CTransform3::ShearY(double d, double f)//Y方向错切变换)和沿Z方向错切变换(void CTransform3::ShearZ(double g, double h)//Z方向错切变换)和矩阵相乘(void CTransform3::MultiplyMatrix(void)//矩阵相乘);

3.使用鼠标或者键盘方向键,与图形进行交互:

使用键盘方向键:当按动左键时,可以转动5,按动右键时,又可以转动5;

使用鼠标键:设置一个定时器,当按动动画按钮(OnGraphAnimation)的时候,可以进行旋转,当再次按动动画按钮(OnUpdateGraphAnimation)的时候,可以停下来。

实现了动画的交互。

三、实现方法

1.设计物体线框模型类:

Pyramid.h:

#pragma once
#include"P3.h"
#include "Facet.h"

class CPyramid
{
public:
	CPyramid(void);
	virtual ~CPyramid(void);
	CP3*  GetVertexArrayName(void);//得到顶点数组名
	void ReadPoint(void);//读入点表
	void ReadFacet(void);//读入面表	
	void Draw(CDC* pDC);//绘制金字塔线框
private:
	CP3 P[5];//点表
	CFacet F[5];//面表
};

Pyramid.cpp:

#include "pch.h"
#include "Pyramid.h"
#define ROUND(d) int(d + 0.5)
CPyramid::CPyramid(void)
{
}

CPyramid::~CPyramid(void)
{
}

CP3* CPyramid::GetVertexArrayName(void)
{
	return	P;
}

void CPyramid::ReadPoint(void)//点表
{	
	P[0].x = 0.0,  P[0].y = 0.0, P[0].z = 1.0;
	P[1].x = 1.0,  P[1].y = 0.0, P[1].z = 1.0;
	P[2].x = 1.0,  P[2].y = 0.0, P[2].z = 0.0;
	P[3].x = 0.0,  P[3].y = 0.0, P[3].z = 0.0;
	P[4].x = 0.5,  P[4].y = 1.0, P[4].z = 0.5;
}

void CPyramid::ReadFacet(void)//面表
{
	F[0].Number = 3, F[0].Index[0] = 0, F[0].Index[1] = 1, F[0].Index[2] = 4;
	F[1].Number = 3, F[1].Index[0] = 1, F[1].Index[1] = 2, F[1].Index[2] = 4;
	F[2].Number = 3, F[2].Index[0] = 2, F[2].Index[1] = 3, F[2].Index[2] = 4;
	F[3].Number = 3, F[3].Index[0] = 0, F[3].Index[1] = 4, F[3].Index[2] = 3;
	F[4].Number = 4, F[4].Index[0] = 0, F[4].Index[1] = 3, F[4].Index[2] = 2, F[4].Index[3] = 1;
}

void CPyramid::Draw(CDC* pDC)
{
	CP2 ScreenPoint, temp;
	for (int nFacet = 0; nFacet < 5; nFacet++)//面循环
	{		
		for (int nPoint = 0; nPoint < F[nFacet].Number; nPoint++)//顶点循环
		{
			ScreenPoint.x = P[F[nFacet].Index[nPoint]].x;
			ScreenPoint.y = P[F[nFacet].Index[nPoint]].y;
			if (0 == nPoint)
			{
				pDC->MoveTo(ROUND(ScreenPoint.x), ROUND(ScreenPoint.y));
				temp = ScreenPoint;
			}
			else
			{
				pDC->LineTo(ROUND(ScreenPoint.x), ROUND(ScreenPoint.y));
			}
		}
		pDC->LineTo(ROUND(temp.x), ROUND(temp.y));//闭合多边形
	}
}

2.使用三维变换类CTransform3类来进行三维的变换

CTransform3.h:

#pragma once
#include"P3.h"

class CTransform3
{
public:
	CTransform3(void);
	virtual ~CTransform3(void);
	void Identity(void);//单位矩阵初始化
	void SetMatrix(CP3* P, int ptNumber);//三维顶点数组初始化
	void Translate(double tx, double ty, double tz);//平移变换
	void Scale(double sx,double sy, double sz);//缩放变换
	void Scale(double sx,double sy, double sz,CP3 p);//相对于任意点的缩放变换
	void Scale(double s);//整体缩放变换
	void Scale(double s, CP3 p);//相对于任意点的整体缩放变换
	void RotateX(double beta);//绕X轴旋转变换
	void RotateY(double beta);//绕Y轴旋转变换
	void RotateZ(double beta);//绕Z轴旋转变换	
	void RotateX(double beta, CP3 p);//相对于任意点的绕X轴旋转变换
	void RotateY(double beta, CP3 p);//相对于任意点的绕Y轴旋转变换
	void RotateZ(double beta, CP3 p);//相对于任意点的绕Z轴旋转变换
	void ReflectX(void);//关于X轴反射变换
	void ReflectY(void);//关于Y轴反射变换
	void ReflectZ(void);//关于Z轴反射变换
	void ReflectXOY(void);//关于XOY面反射变换
	void ReflectYOZ(void);//关于YOZ面反射变换
	void ReflectZOX(void);//关于ZOX面反射变换
	void ShearX(double b, double c);//沿X方向错切变换
	void ShearY(double d, double f);//沿Y方向错切变换
	void ShearZ(double g, double h);//沿Z方向错切变换
	void MultiplyMatrix(void);//矩阵相乘
private:
	double	T[4][4];//三维变换矩阵	
	CP3* P;//三维顶点数组名
	int	ptNumber;//三维顶点个数
};

CTransform3.cpp:

#include "pch.h"
#include "Transform3.h"
#include<math.h>
#define PI 3.1415926



CTransform3::CTransform3(void)
{
}

CTransform3::~CTransform3(void)
{
}

void CTransform3::Identity(void)//单位矩阵初始化
{
	T[0][0] = 1.0, T[0][1] = 0.0, T[0][2] = 0.0, T[0][3] = 0.0;
	T[1][0] = 0.0, T[1][1] = 1.0, T[1][2] = 0.0, T[1][3] = 0.0;
	T[2][0] = 0.0, T[2][1] = 0.0, T[2][2] = 1.0, T[2][3] = 0.0;
	T[3][0] = 0.0, T[3][1] = 0.0, T[3][2] = 0.0, T[3][3] = 1.0;
}

void CTransform3::SetMatrix(CP3* P, int ptNumber)//顶点数组初始化
{
	this->P = P;
	this->ptNumber = ptNumber;
}

void CTransform3::Translate(double tx,double ty,double tz)//平移变换
{
	Identity();
	T[0][3] = tx, T[1][3] = ty, T[2][3] = tz;
	MultiplyMatrix();
}

void CTransform3::Scale(double sx, double sy, double sz)//缩放变换
{
	Identity();
	T[0][0] = sx, T[1][1] = sy, T[2][2] = sz;
	MultiplyMatrix();
}

void CTransform3::Scale(double sx, double sy, double sz, CP3 p)//相对于任意点的缩放变换
{
	Translate(-p.x, -p.y, -p.z);
	Scale(sx, sy, sz);
	Translate(p.x, p.y, p.z);
}

void CTransform3::Scale(double s)//整体缩放变换
{
	Identity();
	T[0][0] = s, T[1][1] = s, T[2][2] = s;
	MultiplyMatrix();
}

void CTransform3::Scale(double s, CP3 p)//相对于任意点的整体缩放变换
{
	Translate(-p.x, -p.y, -p.z);
	Scale(s);
	Translate(p.x, p.y, p.z);
}

void CTransform3::RotateX(double beta)//绕X轴旋转变换
{
	Identity();
	beta = beta * PI / 180;
	T[1][1] = cos(beta), T[1][2] = -sin(beta);
	T[2][1] = sin(beta), T[2][2] = cos(beta);
	MultiplyMatrix();
}

void CTransform3::RotateY(double beta)//绕Y轴旋转变换
{
	Identity();
	beta = beta * PI/180;
	T[0][0] = cos(beta), T[0][2] = sin(beta);
	T[2][0] =-sin(beta), T[2][2] = cos(beta);
	MultiplyMatrix();
}

void CTransform3::RotateZ(double beta)//绕Z轴旋转变换
{
	Identity();
	beta = beta * PI/180;
	T[0][0] = cos(beta), T[0][1] = -sin(beta);
	T[1][0] = sin(beta), T[1][1] = cos(beta);
	MultiplyMatrix();
}

void CTransform3::RotateX(double beta, CP3 p)//相对于任意点的X轴旋转变换
{
	Translate(-p.x, -p.y, -p.z);
	RotateX(beta);
	Translate(p.x, p.y, p.z);
}

void CTransform3::RotateY(double beta, CP3 p)//相对于任意点的Y轴旋转变换
{
	Translate(-p.x, -p.y, -p.z);
	RotateY(beta);
	Translate(p.x, p.y, p.z);
}

void CTransform3::RotateZ(double beta, CP3 p)//相对于任意点的Z轴旋转变换
{
	Translate(-p.x, -p.y, -p.z);
	RotateZ(beta);
	Translate(p.x, p.y, p.z);
}

void CTransform3::ReflectX(void)//X轴反射变换
{
	Identity();
	T[1][1] = -1, T[2][2] = -1;
	MultiplyMatrix();
}

void CTransform3::ReflectY(void)//Y轴反射变换
{
	Identity();
	T[0][0] = -1, T[2][2] = -1;
	MultiplyMatrix();
}

void CTransform3::ReflectZ(void)//Z轴反射变换
{
	Identity();
	T[0][0] = -1, T[1][1] = -1;
	MultiplyMatrix();
}

void CTransform3::ReflectXOY(void)//XOY面反射变换
{
	Identity();
	T[2][2] = -1;
	MultiplyMatrix();
}

void CTransform3::ReflectYOZ(void)//YOZ面反射变换
{
	Identity();
	T[0][0] = -1;
	MultiplyMatrix();
}

void CTransform3::ReflectZOX(void)//XOZ面反射变换
{
	Identity();
	T[1][1] = -1;
	MultiplyMatrix();
}
	
void CTransform3::ShearX(double b, double c)//X方向错切变换
{
	Identity();
	T[0][1] = b, T[0][2] = c;
	MultiplyMatrix();
}

void CTransform3::ShearY(double d, double f)//Y方向错切变换
{
	Identity();
	T[1][0] = d, T[1][2] = f;
	MultiplyMatrix();
}

void CTransform3::ShearZ(double g, double h)//Z方向错切变换
{
	Identity();
	T[2][0] = g;T[2][1] = h;
	MultiplyMatrix();
}

void CTransform3::MultiplyMatrix(void)//矩阵相乘
{
	CP3* PTemp = new CP3[ptNumber];
	for(int i = 0;i < ptNumber;i++)
		PTemp[i] = P[i];
	for(int i = 0;i < ptNumber;i++)
	{	
		P[i].x = T[0][0] * PTemp[i].x + T[0][1] * PTemp[i].y + T[0][2] * PTemp[i].z + T[0][3] * PTemp[i].w;
		P[i].y = T[1][0] * PTemp[i].x + T[1][1] * PTemp[i].y + T[1][2] * PTemp[i].z + T[1][3] * PTemp[i].w;
		P[i].z = T[2][0] * PTemp[i].x + T[2][1] * PTemp[i].y + T[2][2] * PTemp[i].z + T[2][3] * PTemp[i].w;
		P[i].w = T[3][0] * PTemp[i].x + T[3][1] * PTemp[i].y + T[3][2] * PTemp[i].z + T[3][3] * PTemp[i].w;
	}
	delete []PTemp;
}

3.使用鼠标或者键盘方向键,与图形进行交互:

void CTestView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (!bPlay)
	{
		switch (nChar)
		{
		case VK_UP:
			Alpha = -5;
			transform.RotateX(Alpha);
			break;
		case VK_DOWN:
			Alpha = +5;
			transform.RotateX(Alpha);
			break;
		case VK_LEFT:
			Beta = -5;
			transform.RotateY(Beta);
			break;
		case VK_RIGHT:
			Beta = +5;
			transform.RotateY(Beta);
			break;
		default:
			break;
		}
		Invalidate(FALSE);
	}
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}


void CTestView::OnGraphAnimation()
{
	// TODO: 在此添加命令处理程序代码
	bPlay = !bPlay;
	if (bPlay)//设置定时器
		SetTimer(1, 300, NULL);
	else
		KillTimer(1);
}

void CTestView::OnUpdateGraphAnimation(CCmdUI *pCmdUI)
{
	// TODO: 在此添加命令更新用户界面处理程序代码
	if (bPlay)
		pCmdUI->SetCheck(TRUE);
	else
		pCmdUI->SetCheck(FALSE);
}

在TestView.h中定义:

protected:
	CPyramid pyramid;
	CTransform3 transform;//变换对象
	double Alpha, Beta;//x方向旋转α角,y方向旋转β角
	BOOL bPlay;//动画开关
public:
	void DoubleBuffer(CDC* pDC);//双缓冲绘图
	void DrawObject(CDC* pDC);//绘制金字塔线框

在TestView.cpp中绘制三维变换:

CTestView::CTestView() noexcept
{
	// TODO: 在此处添加构造代码
	bPlay = FALSE;
	pyramid.ReadPoint();
	pyramid.ReadFacet();
	transform.SetMatrix(pyramid.GetVertexArrayName(), 5);
	double nEdge = 400;
	transform.Scale(nEdge, nEdge, nEdge);//比例变换
	transform.Translate(-nEdge / 2, -nEdge / 2, -nEdge / 2);//平移变换
}

绘制图形:

void CTestView::OnDraw(CDC* pDC)
{
	CTestDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	DoubleBuffer(pDC);//绘制图形
}

DoubleBuffer函数:

void CTestView::DoubleBuffer(CDC* pDC)//双缓冲绘图
{
	CRect rect;
	GetClientRect(&rect);
	pDC->SetMapMode(MM_ANISOTROPIC);
	pDC->SetWindowExt(rect.Width(), rect.Height());
	pDC->SetViewportExt(rect.Width(), -rect.Height());
	pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
	CDC memDC;//声明内存DC
	memDC.CreateCompatibleDC(pDC);//创建一个与显示DC兼容的内存DC
	CBitmap NewBitmap, *pOldBitmap;
	NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());//创建兼容内存位图 
	pOldBitmap = memDC.SelectObject(&NewBitmap);//将兼容位图选入内存DC
	memDC.FillSolidRect(rect, pDC->GetBkColor());//设置客户区背景色
	rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);
	memDC.SetMapMode(MM_ANISOTROPIC);//内存DC自定义坐标系
	memDC.SetWindowExt(rect.Width(), rect.Height());
	memDC.SetViewportExt(rect.Width(), -rect.Height());
	memDC.SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
	DrawObject(&memDC);//绘制图形
	pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, -rect.Width() / 2, -rect.Height() / 2, SRCCOPY); //将内存DC中的位图拷贝到显示DC
	memDC.SelectObject(pOldBitmap);
	NewBitmap.DeleteObject();
	memDC.DeleteDC();
}

四、实验结果分析

实验结果

实验参考

计算机图形学

实验总结

在这次实验中,使用三维变换可以使物体看起来有立体感,刚开始进行代码编译的时候,感觉不是很熟悉就会感觉很陌生,在进行多次编译后,慢慢自己找代码错误,就开始熟悉自己的编译过程,自己建文档,自己建代码的开始,还是感觉有意思,但是经常出错就会很烦解决不了,但是最终都解决啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值