EGE基础入门篇(七):组合图形

EGE专栏:EGE专栏

上一篇:EGE基础入门篇(六):基本图形

下一篇:EGE基础入门篇(八):清屏与重绘


一、 组合图形

1. 复杂图形由基本图形组合而成

  复杂的图形可以由基本图形组合而成,如多边形可以由多个三角形组合而成。
  同一个图形也可由基本图形以不同的方式进行组合。并且可以利用后绘制的图形会将先绘制图形覆盖的原理。
在这里插入图片描述

2. 绘制的顺序性

  图形的绘制是有先后顺序的,并不能随意地按任意顺序进行绘制。
  当后绘制的图形和先绘制的图形重叠时,如果图形不透明,那么先绘制的图形会被后绘制的图形所覆盖。即使图形是透明的,也会造成混合后的颜色不相同。

  如下图所示,当物体存在一定的遮挡关系时,需要按由远到近的顺序,先画远处的物体,再画近处的物体,这样绘制出的图形才会符合实际。否则,图中的树木会被草地和高山所覆盖。
在这里插入图片描述
  在三维物体渲染中,深度排序算法也叫画家算法,通过先对物体按照离视点远近的顺序进行排序,再将物体按顺序进行绘制的方法来得到正确的渲染结果。
在这里插入图片描述
  如下图所示,图中为通过调整结点和边的绘制顺序,得到了两种不同的绘制结果,而右边按先边后结点顺序绘制出的二叉树图形才是我们想要的。
在这里插入图片描述

二、组合图形的绘制

  

1. 彭罗斯三角形

  彭罗斯三角形是利用视觉错觉构造的一种不可能图形,在三维空间中并不真实客观存在。在二维空间中,可以在某个特定角度观察到这种"不可能图形",所以彭罗斯三角形是二维图形的三维投射形成的光学错觉。彭罗斯三角形在《纪念碑谷》中有着大量的运用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  在二维绘图中,有多种绘图方式,既可以将彭罗斯三角形看作是三个互不重叠的多边形组合而成,也可以是特殊构造的三维图形的投影(纪念碑谷中的做法,适合三维模型),还可以改变三维物体表面投影的遮挡关系来形成。
  这里我们通过构造三维模型,利用其投影来绘制彭罗斯三角形。
  我们先根据物体在三维坐标系中的位置得到其顶点的三维坐标,然后将其投影到平面上,得到在平面上的二维坐标,然后在变换到窗口坐标系上,即可得到多边形每个顶点的坐标,调用ege_fillpoly() 绘图即可。

在这里插入图片描述

  我们对一个单位立方体进行分析,可以发现立方体被投影成了正六边形。由于投影面垂直于向量 v v v,与 v v v垂直的线段投影后长度不变。从图中可以看出平面ABC垂直于向量 v v v,所以投影后 A B 、 A C 和 B C AB、AC和BC ABACBC长度均不变,长度为 2 \sqrt{2} 2 。通过对在坐标轴上的三个点A、B、D映射关系分析,我们就能得到相应的变换关系。
  三维空间上的顶变换后到 x O y xOy xOy平面后,再通过视口变换转换成窗口坐标系上的坐标,就可以利用EGE中的绘图函数进行绘图了。
在这里插入图片描述

完整代码如下:

#include <graphics.h>
#include <cmath>

typedef struct Rect
{
	float x, y;
	float width, height;
} Rect;

typedef struct Vertex
{
	float x, y, z;
} Vertex;

//投影到xOy平面上
ege_point vertexPrejection(Vertex vertex)
{
	static const double sqrtOf2 = sqrt(2.0);
	static const double sqrtOf3 = sqrt(3.0);
	static const double sqrtOf6 = sqrt(6.0);
	// 根据点的投影:
	// (1, 0, 0)映射到(√2/2,  √6/6, 0)
	// (0, 1, 0)映射到(0,     √6/3, 0)
	// (0, 0, 1)映射到(√2/2, -√6/6, 0)
	// 得到:变换矩阵(坐标为列向量)
	// |x| = |√2/2,    0,  √2/2| |x|
	// |y| = |√6/6, √6/3, -√6/6| |y|
	// |z| = |   0,    0,     0| |z|

	ege_point point;
	point.x = vertex.x * (sqrtOf2 / 2.0) + vertex.z * (sqrtOf2 / 2.0);
	point.y = vertex.x * (sqrtOf6 / 6.0) + vertex.y * (sqrtOf6 / 3.0) + vertex.z * (-sqrtOf6 / 6.0);
	return point;
}

int main() {
	//定义窗口大小,以及窗口中心
	int winWidth = 640, winHeight = 640;
	Rect viewRect = { -2.0f, -2.0f, 8.0f, 8.0f };		//投影截取范围:左下角(-2, -2),宽高为8x8的矩形区域
	int xCenter = winWidth / 2, yCenter = winHeight / 2;

	initgraph(winWidth, winHeight, INIT_RENDERMANUAL);
	ege_enable_aa(true);
	setbkcolor(EGERGB(0xFF, 0xFF, 0xFF));

	//三个多边形的顶点坐标(凹六边形)
	Vertex vertices[3][6] = {
		{
			{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 5.0f, 1.0f}, 
			{0.0f, 5.0f, 4.0f}, {0.0f, 6.0f, 5.0f}, {0.0f, 6.0f, 0.0f},
		},
		{
			{0.0, 0.0f, 1.0f}, {6.0f, 0.0f, 1.0f}, {6.0f, 1.0f, 1.0f},
			{1.0, 1.0f, 1.0f}, {1.0f, 4.0f, 1.0f}, {0.0f, 5.0f, 1.0f},
		},
		{
			{0.0f, 6.0f, 0.0f}, {0.0f, 6.0f, 5.0f}, {2.0f, 1.0f, 0.0f},
			{1.0f, 1.0f, 1.0f}, {6.0f, 1.0f, 1.0f}, {1.0f, 6.0f, 0.0f},
		},
	};

	//多边形的颜色
	color_t colors[3] = { EGEARGB(255, 2, 118, 191), EGEARGB(255, 94, 175, 228),EGEARGB(255, 146, 202, 239) };

	const int numPolys = 3;
	const int numPoints = 6;
	
	//计算缩放比例
	int xratio = (float)winWidth / viewRect.width;
	int yratio = (float)winHeight / viewRect.height;

	ege_point points[6];
	
	//将三维空间中的多边形顶点先投影到二维平面上,再转到窗口坐标系中,然后将多边形绘制出来
	for (int i = 0; i < numPolys; i++) {
		
		for (int j = 0; j < numPoints; j++) {
			//三维空间中的坐标投影到xOy平面上
			points[j]   = vertexPrejection(vertices[i][j]);

			//视口变换:ViewRect 变换到窗口[(0, 0), winWidth, winHeight]
			points[j].x = (points[j].x - viewRect.x) * xratio;
			points[j].y = winHeight - (points[j].y - viewRect.y) * yratio;
		}

		//设置颜色,绘制多边形
		setfillcolor(colors[i]);
		ege_fillpoly(numPoints, points);
	}

	getch();

	closegraph();
	return 0;
}

运行结果如图:
在这里插入图片描述

2. 国际象棋棋盘

在这里插入图片描述
  国际象棋棋盘为正方形,由64个黑白(深色与浅色)相间的格子组成;棋子分黑白(深色与浅色)两方共32枚,每方各16枚。棋盘格子颜色分深色浅色两种,颜色并不固定。每个格子的绘制只需要计算出左上角坐标格子宽高即可,然后使用填充矩形来绘制。

  先对每个格子进行坐标定义,用 (列号 c o l col col, 行号 r o w row row) 来表示每个格子 (行号row :0 ~ 7,列号col:0 ~ 7),可以得到格子左上角在窗口上的坐标 ( l e f t , t o p ) (left, top) (left,top)
窗 口 坐 标 ( l e f t , t o p ) = ( 列 号 c o l ⋅ 列 宽 w i d t h , 行 号 r o w ⋅ 行 高 h e i g h t ) 窗口坐标(left,top) = (列号col \cdot 列宽width, 行号row \cdot 行高height) (left,top)=(colwidth,rowheight)  调用ege_fillrect(left, top, width, height) 即可进行绘制。
在这里插入图片描述
完整代码如下

#include <graphics.h>
#include <cmath>


int main() {
	//定义格子宽高及格子行列数
	int gridWidth = 80, gridHeight = 80;
	int numCol = 8, numRow = 8;

	//计算窗口宽高及中心位置
	int winWidth = numCol * gridWidth, winHeight = numRow * gridHeight;
	int xCenter = winWidth / 2, yCenter = winHeight / 2;

	//创建窗口
	initgraph(winWidth, winHeight, INIT_RENDERMANUAL);
	ege_enable_aa(true);

	setbkcolor_f(EGERGB(0xFF, 0xFF, 0xFF));

	//定义深色和浅色
	color_t darkColor  = EGEARGB(255, 34, 34, 34);
	color_t lightColor = EGEARGB(255, 216, 216, 216);
	
	//绘制格子
	for (int row = 0; row < numRow; row++) {
		for (int col = 0; col < numCol; col++) {
			int x = col * gridWidth, y = row * gridHeight;
			color_t gridColor = darkColor;

			//根据格子位置确定颜色()
			if ((row + col) % 2 == 0) {
				gridColor = lightColor;
			}

			//绘制格子
			setfillcolor(gridColor);
			ege_fillrect(x, y, gridWidth, gridHeight);
		}
	}

	getch();

	closegraph();
	return 0;
}

运行结果如图

在这里插入图片描述


EGE专栏:EGE专栏

上一篇:EGE基础入门篇(六):基本图形

下一篇:EGE基础入门篇(八):清屏与重绘

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

依稀_yixy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值