前言

为什么三角形是实时渲染的首选多边形?因为它们具有以下理想的特性:

  • 三角形是最简单的多边形类型。少于三个顶点就没法组成一个多边形。
  • 三角形始终是平面的。任何具有四个或更多顶点的多边形都没有这个属性,因为前三个顶点定义了一个平面,第四个顶点可能位于该平面的上方或下方。
  • 三角形在大多数类型的变换下仍然是三角形,包括仿射变换和透视投影。
  • 几乎所有商业图形加速硬件都是围绕三角形光栅化设计的。

绘制线框三角形

上篇文章我们实现了画线功能,现在可以轻而易举的画一个三角形了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>
#include <vector>

//插值
std::vector<float> Interpolate(float i0, float d0, float i1, float d1)
{
std::vector<float> values;
if (glm::abs(i0 - i1)<1e-6)
{
values.push_back(d0);
return values;
}
float a = (d1 - d0) / (i1 - i0);
float d = d0;
for (int i = i0;i < i1;i++)
{
values.push_back(d);
d = d + a;
}
return values;
}

//画线
void DrawLine(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (glm::abs(P1.x-P0.x) > glm::abs(P1.y-P0.y))
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
std::vector<float> ys = Interpolate(P0.x,P0.y,P1.x,P1.y);
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, ys[x-P0.x], color);
}
}
else
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
std::vector<float> xs = Interpolate(P0.y, P0.x, P1.y, P1.x);
for (int y = P0.y;y < P1.y;y++)
{
putpixel(xs[y - P0.y], y, color);
}
}

}

//画线框三角形
void DrawWireframeTriangle(glm::vec2 P0, glm::vec2 P1, glm::vec2 P2, COLORREF color)
{
DrawLine(P0, P1, color);
DrawLine(P1, P2, color);
DrawLine(P2, P0, color);
}

//画填充三角形
void DrawFilledTriangle(glm::vec2 P0, glm::vec2 P1, glm::vec2 P2, COLORREF color)
{
//排序顶点 P0.y <= P1.y <= P2.y
if (P1.y < P0.y) { std::swap(P1, P0); }
if (P2.y < P0.y) { std::swap(P2, P0); }
if (P2.y < P1.y) { std::swap(P2, P1); }

//------------------P2|\
//--------------------| \
//--------------------| \ P1
//--------------------| /
//--------------------| /
//------------------P0|/

//P0P1边x坐标数组
std::vector<float> x01 = Interpolate(P0.y, P0.x, P1.y, P1.x);
//P1P2边x坐标数组
std::vector<float> x12 = Interpolate(P1.y, P1.x, P2.y, P2.x);
//P0P2边x坐标数组
std::vector<float> x02 = Interpolate(P0.y, P0.x, P2.y, P2.x);

//【注意】去掉重复坐标,P0P1和P1P2重复了P1
//x01.pop_back();
//x012=x01+x12 x012代表P0P1和P1P2两条边的x坐标数组
x01.insert(x01.end(), x12.begin(), x12.end());
std::vector<float> x012(x01);

float m = glm::floor(x012.size() / 2);
std::vector<float> x_left;
std::vector<float> x_right;
//-------第一种情况
//---------P2|\
//-----------| \
//-----------| \ P1
//-----------| /
//-----------| /
//---------P0|/
if (x02[m] < x012[m])
{
x_left = x02;
x_right = x012;
}
//-------第二种情况
//----------/|P2
//---------/ |
//------p1/ |
//--------\ |
//---------\ |
//----------\|P0
else
{
x_left = x012;
x_right = x02;
}
//从上到下,从左到右填充
for (int y = P0.y;y < P2.y;y++)
{
for (int x = x_left[y - P0.y];x < x_right[y - P0.y];x++)
{
putpixel(x, y, color);
}
}
}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

glm::vec2 P0(200, 200);
glm::vec2 P1(200, 500);
glm::vec2 P2(350, 350);

DrawFilledTriangle(P0, P1, P2, RED);

_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果

在这里插入图片描述

结尾

如果你遇到了问题可以在评论区告诉我哦
项目地址