大家好,欢迎来到IT知识分享网。
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 AB、AC和BC长度均不变,长度为 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)=(列号col⋅列宽width,行号row⋅行高height) 调用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基础入门篇(八):清屏与重绘
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/27311.html