【计算机图形学】实验一:二维图形绘制

写在前面

效果演示移步视频

https://www.bilibili.com/video/BV1ah4y177dL

1. 实验内容

1.绘制金刚石图案

金刚石图案的成图规则是:把一个圆周等分成 n n n份,然后每两点之间连线。当 n n n取奇数时,该图案可一笔连续绘成,即用MoveTo函数确定一个当前点,然后连续用LineTo函数连点成线。
请设计连线规则并编程实现。

2.绘制魔术三角形

绘制下图所示的魔术三角形图案 ,采用三种可明显区分的颜色填充。
在这里插入图片描述

3.绘制递归圆

应用递归的方法绘制如下所示的图案。
在这里插入图片描述

2. 实验环境

Visual Studio 2019
图形学实验程序框架
Windows11系统

3. 问题分析

3.1问题1

对于问题1,首先需要计算出图案上各个像素点的坐标,设图案上有 n n n个点,图案中心点坐标 ( x m , y m ) (x_m,y_m) (xm,ym),记图案上每个顶点坐标为 ( x i , y i ) (x_i,y_i) (xi,yi), i = 0 , 1 , 2... n − 1 i=0,1,2...n-1 i=0,1,2...n1,则即每个顶点相对于中心点连线的夹角为 θ = 2 π n \theta=\frac{2\pi}n θ=n2π,则 x i = x m + c o s ( i θ ) x_i=x_m+cos(i\theta) xi=xm+cos(iθ) y i = y m + s i n ( i θ ) y_i=y_m+sin(i\theta) yi=ym+sin(iθ)。第二步就是将点连接起来。当且仅当 n n n为奇数时,可以使用一笔画算法。一笔画算法如下:记录当前点为 p o s pos pos。从0号点出发,大循环 n n n次。每次大循环中,小循环画直线。先隔0个点画一条直线,再隔1个点画一条直线,隔2个点画一条直线…直到小循环的最后是隔 n / 2 − 1 n/2-1 n/21个点画直线。设本次隔了 i i i个点画了直线,则目标点 j = ( p o s + 1 + i ) m o d    n j=(pos+1+i)\mod n j=(pos+1+i)modn。直到大循环结束,整个金刚石图案也绘制完成。

3.2问题2

对于问题2,可以预先处理出12个点位的坐标。点位标号如下:
在这里插入图片描述
初始时,设0点的坐标为 ( 400 , 800 ) (400,800) (400,800),长边长度500,短边长度100.则各个点坐标如下表所示:

点位编号 x x x坐标 y y y坐标所属区域点位编号 x x x坐标 y y y坐标所属区域
0400800红区6750713红区、紫区
1900800红区、黄区7700454红区、黄区
2950713黄区8500627紫区、黄区
3700281黄区、紫区9700627紫区
4600281紫区10650541红区
5350713红区、紫区11600627黄区

创建三个多边形,每个多边形表示魔术三角形的一个同色区域。多边形1按顺序将点0、点1、点7、点10、点6、点5连接。多边形2按顺序将点1、点2、点3、点8、点7、点11连接。多边形3将点3、点4、点5、点6、点9、点8连接。
为了实现变色的效果,每次都需要将每一个多边形重新赋值颜色并重新上色。在选取颜色时,选择使用随机的颜色。

3.3问题3

对于问题3,首先绘制出中心圆。之后定义一个函数circle,参数为递归的深度、小圆的半径、小圆的圆心 x x x坐标、小圆的圆心 y y y坐标、CDC类。中心圆外8个方向需要小圆。取中心圆半径为 r r r为大圆半径的1/4,坐标 ( x m , y m ) (x_m,y_m) (xm,ym),则每个小圆圆心坐标为 x i = 2 r cos ⁡ ( i θ ) x_i=2r\cos(i\theta) xi=2rcos(iθ), y i = 2 r sin ⁡ ( i θ ) y_i=2r\sin(i\theta) yi=2rsin(iθ),其中 i = 0 , 1 , 2 , . . . 7 i=0,1,2,...7 i=0,1,2,...7 θ = 4 5 ∘ ∗ 2 π / 180 \theta=45^{\circ}*2\pi/180 θ=452π/180。对于每个小圆,同样递归地再去画小圆的小圆,直到当前圆的递归深度为0。

4. 算法设计

4.1问题1

正如3.1所分析,算法的流程如下:
1.计算金刚石图案中各个点的坐标。
2.运行一笔画算法。记录当前点为 p o s pos pos。从0号点出发,大循环 n n n次。每次大循环中,小循环画直线。先隔0个点画一条直线,再隔1个点画一条直线,隔2个点画一条直线…直到小循环的最后是隔 n / 2 − 1 n/2-1 n/21个点画直线。小循环内部,设本次隔了 i i i个点画了直线,则目标点 j = ( p o s + i + 1 ) m o d    n j=(pos+i+1)\mod n j=(pos+i+1)modn。从 p o s pos pos j j j连一条直线。最后,将 p o s pos pos赋值为 j j j,完成小循环。完成 n n n次大循环后,算法成功结束。
流程图如下:
在这里插入图片描述

4.2问题2

正如章节3.2分析,算法的流程如下:
1.计算各个点位坐标,确定魔术三角形每个部分用到了哪些模块。
2.每隔一段时间给三个部分的颜色重新绘制上色,颜色为随机颜色。这一过程不断地循环进行。
在这里插入图片描述

4.3问题3

正如3.3所分析,算法的流程如下:
先创建pDC对象和画笔,然后递归地调用函数。在函数体内,先判断递归深度。若递归深度<=0,则退出,返回上一层。若递归深度>0,则找到递归圆圆心的坐标与半径,根据当前圆心与半径画出当前圆。再找到当前圆附属的8个小圆的坐标,递归地画小圆。小圆的半径为大圆的1/4,小圆的递归层数为大圆的递归层数-1。在画完所有圆后,算法结束。
在这里插入图片描述

5. 源代码

5.1金刚石

void CDiamondView::DrawDiamond(int nVertex, int radius,int millisecond){
	InvalidateRect(NULL);//强制清屏
	UpdateWindow();
	struct Point {
		double x, y;
	};
	Point* pnt = new Point[nVertex];
	const double PI = 3.14159265;
	CDC* pDC = GetDC();
	CRect rgn;
	GetClientRect(&rgn);
	CBrush newBrush;
	newBrush.CreateSolidBrush(RGB(255, 255, 255));
	pDC->FillRect(&rgn, &newBrush);
	CPen redPen(PS_SOLID, 1, RGB(0, 0, 255));
	CPen* OldPen = pDC->SelectObject(&redPen);
	double theta = 2 * PI / nVertex;
	for (int i = 0; i < nVertex; i++) {
		pnt[i].x = radius * cos(i * theta) + MaxX() / 2;
		pnt[i].y = radius * sin(i * theta) + MaxY() / 2;
	}
	int pos = 0;
	if (nVertex % 2 == 1) {
		for (int loop = 0; loop < nVertex; loop++) {
			for (int interv = 0; interv <= nVertex / 2 - 1; interv++) {//间隔的点的数量
				int nextpos = (pos + 1 + interv) % nVertex;
				pDC->MoveTo(round(pnt[pos].x), round(pnt[pos].y));
				pDC->LineTo(round(pnt[nextpos].x), round(pnt[nextpos].y));
				pos = nextpos;
				Sleep(millisecond);
			}
		}
	}
	else {
		for (int i = 0; i < nVertex-1; i++) {
			for (int j = i + 1; j < nVertex; j++) {
				pDC->MoveTo(round(pnt[i].x), round(pnt[i].y));
				pDC->LineTo(round(pnt[j].x), round(pnt[j].y));
				Sleep(millisecond);
			}
		}
	}
	pDC->SelectObject(OldPen);
}

5.2魔术三角形

//绘制魔术三角
void CDiamondView::DrawTriangle(){
	InvalidateRect(NULL);//强制清屏
	UpdateWindow();
	CDC* pDC = GetDC();
	CRect rect;
	GetClientRect(&rect);
	CBrush br(RGB(0, 0, 0));
	pDC->FillRect(&rect, &br);//设置背景为黑
	CBrush newBrush, * oldBrush;
	POINT vertex[12] =
	{ {400,800},
		{900,800},
		{950,713},
		{700,281},
		{600,281},
		{350,713},
		{750,713},
		{700,454},
		{500,627},
		{700,627},
		{650,541},
		{600,627} };
	POINT vertex1[6] = { vertex[0],vertex[1],vertex[7],vertex[10],vertex[6],vertex[5] };
	POINT vertex2[6] = { vertex[1],vertex[2],vertex[3],vertex[8],vertex[11],vertex[7] };
	POINT vertex3[6] = { vertex[3],vertex[4],vertex[5],vertex[6],vertex[9],vertex[8] };
	int t = 600;
	while (t--) {
		//第一个填充
		{
			CDC* pDC = GetDC();
			CBrush newBrush, * oldBrush;
			newBrush.CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
			oldBrush = pDC->SelectObject(&newBrush);
			pDC->SetPolyFillMode(WINDING);
			pDC->Polygon(vertex1, 6);
			pDC->SelectObject(oldBrush);
			newBrush.DeleteObject();
		}
		//第二个填充
		{
			CDC* pDC = GetDC();
			CBrush newBrush, * oldBrush;
			newBrush.CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
			oldBrush = pDC->SelectObject(&newBrush);
			pDC->SetPolyFillMode(WINDING);
			pDC->Polygon(vertex2, 6);
			pDC->SelectObject(oldBrush);
			newBrush.DeleteObject();
		}
		//第三个填充
		{
			CDC* pDC = GetDC();
			CBrush newBrush, * oldBrush;
			newBrush.CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
			oldBrush = pDC->SelectObject(&newBrush);
			pDC->SetPolyFillMode(WINDING);
			pDC->Polygon(vertex3, 6);
			pDC->SelectObject(oldBrush);
			newBrush.DeleteObject();
		}
		Sleep(100);
	}
}

5.3 递归圆

//绘制递归圆
//nDepth:递归深度
void circle(int n, double r, double x0, double y0, CDC* pDC) {
	if (n == 0) 
		return;
	const double PI = 3.14159;
	double theta = 2*PI/8;
	double x, y;
	for (int i = 0; i < 8; i++)
	{
		x = 2 * r * cos(i * theta) + x0;
		y = 2 * r * sin(i * theta) + y0;
		CPen newPen, * oldPen;
		newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
		oldPen = pDC->SelectObject(&newPen);
		CRect rect1(x - 0.25 * r, y - 0.25 * r, x + 0.25 * r, y + 0.25 * r);
		circle(n - 1, 0.25 * r, x, y, pDC);
		pDC->Ellipse(&rect1);
	}
}
//绘制递归圆主函数
//nDepth:递归深度

void CDiamondView::DrawRecursionCircle(int nDepth)
{
	InvalidateRect(NULL);//强制清屏
	UpdateWindow();
	if (nDepth == 0) 
		return;
	int x0 = MaxX()/2, y0 =MaxY()/2;//圆心
	int r = 160;//半径
	const double PI = 3.14159;
	CDC* pDC = GetDC();
	CPen newPen, * oldPen;
	newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
	oldPen = pDC->SelectObject(&newPen);
	CRect rect(x0 - r, y0 - r, x0 + r, y0 + r);
	pDC->Ellipse(&rect);
	circle(nDepth, r, x0, y0, pDC);
}

6.程序运行结果

在这里插入图片描述
正确地绘制出了金刚石图案,且实现了一笔画的效果。在此图中,含有21个点,半径为150,图案中心在屏幕的中心。
在这里插入图片描述
正确地绘制出了魔术三角形。在每一秒,三角形三个部分的颜色都会随机变换一次。
在这里插入图片描述
正确地绘制出了递归深度为3的递归圆。

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实验OpenGL+GLUT开发平台搭建 5 小实验1: 开发环境设置 5 小实验2: 控制窗口位置和大小 6 小实验3:默认的可视化范围 6 小实验4:自定义可视化范围 7 小实验5: 几何对象变形的原因 8 小实验6: 视口坐标系及视口定义 8 小实验7:动态调整长宽比例,保证几何对象不变形 9 实验二 动画和交互 10 小实验1: 单缓冲动画技术 10 小实验2: 双缓冲动画技术 11 小实验3:键盘控制 13 小实验4:鼠标控制【试着单击鼠标左键或者右键,试着按下鼠标左键后再移动】 14 实验三 几何变换、观察变换、三维对象 16 小实验1:二维几何变换 16 小实验2:建模观察(MODELVIEW)矩阵堆栈 17 小实验3:正平行投影1 19 小实验4:正平行投影2 19 小实验5:正平行投影3 20 小实验6:透射投影1 21 小实验6:透射投影2 22 小实验7:三维对象 24 实验四 光照模型和纹理映射 26 小实验1:光照模型1----OpenGL简单光照效果的关键步骤。 26 小实验2:光照模型2----光源位置的问题 28 小实验3:光照模型3----光源位置的问题 31 小实验4:光照模型4----光源位置的问题 33 小实验5:光照模型5----光源位置的问题 35 小实验6:光照模型6----光源位置的问题 38 小实验7:光照模型7----光源位置的动态变化 40 小实验8:光照模型8----光源位置的动态变化 43 小实验9:光照模型9---光源位置的动态变化 45 小实验10:光照模型10---聚光灯效果模拟 48 小实验11:光照模型11---多光源效果模拟 50 小实验12:光照效果和雾效果的结合 53 小实验13:纹理映射初步—掌握OpenGL纹理映射的一般步骤 56 小实验13:纹理映射—纹理坐标的自动生成(基于参数的曲面映射) 59 小实验14:纹理映射—纹理坐标的自动生成(基于参考面距离) 61

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zombo_tany

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值