跟我一起學圖形編程
作者:姚明 聯系方式:alanvincentmail@gmail.com 2011年1月26日 17:42:15
點,線,面,我們這節課學學面的填充和生成。千萬不要忽略這節課的重要性,我們平時看到的3維圖形,一般是由三角網格構成,比如,一個人,一座山,一張桌子。網格被填充了顏色后,就是我們看到的栩栩如生的樣子。但是,它們不是被簡單的填充單一的顏色,被填充的顏色是經過紋理,光照,材質等運算后得到的結果,這是一個復雜的過程,專業術語稱其為象素著色。這些內容,以后的課程我們慢慢深入學習。在這里,我們只用隨機顏色的線條簡單的填充整個多邊形表面。
理論:
填充的算法大概分為兩種思路,第一種,掃描線法,想象一下,從一個封閉多邊形外面一點開始,畫一直線,與多邊形相交,通過檢測掃描線上每點的狀態,就能區分出,多邊形外部和內部的點。第二種,種子算法,先取多邊形內部任意一點做種子,由這個種子,向左右,上下擴散,最終填充整個多邊形內部。我認為,掃描線算法適用于,多邊形各頂點值已知,即邊界已知情況下的填充。種子算法適用于,邊界未知情況,例如,屏幕上有多個多邊形重疊區域的填充。詳細的算法細節請參考點擊下載的相關章節。下面給出掃描線算法的實現代碼。
內容:
1
//-----------------------------------------------------------------------------------------------
2
// 功能: 填充多邊形
3
//
4
// 參數: lpPoints: 指向頂點坐標數組的指針,數組類型為POINT,多邊形由它們順次封閉連接得到
5
// nCount: 頂點的個數
6
// nColor: 填充的顏色 默認為黑色
7
// DC: 設備句柄
8
//
9
// 返回: 無返回值
10
//
11
// 說明: 可以是邊相交的多邊形
12
//
13
// 創建(修改): 2011-1-13 16:31 姚明
14
//-----------------------------------------------------------------------------------------------
15
void FillPolygon(LPPOINT lpPoints, int nCount, int nColor /**//*=0*/,HDC &DC)
16

{
17
// 邊結構數據類型
18
typedef struct Edge
19
{
20
int ymax; // 邊的最大y坐標
21
float x; // 與當前掃描線的交點x坐標
22
float dx; // 邊所在直線斜率的倒數
23
struct Edge *pNext; // 指向下一條邊
24
} Edge, *LPEdge;
25
26
int i = 0, j = 0, k = 0;
27
int y0 = 0, y1 = 0; // 掃描線的最大和最小y坐標
28
LPEdge pAET = NULL; // 活化邊表頭指針
29
LPEdge *pET = NULL; // 邊表頭指針
30
31
pAET = new Edge; // 初始化表頭指針,第一個元素不用
32
pAET->pNext = NULL;
33
34
// 獲取y方向掃描線邊界
35
y0 = y1 = lpPoints[0].y;
36
for (i = 1; i < nCount; i++)
37
{
38
if (lpPoints[i].y < y0)
39
y0 = lpPoints[i].y;
40
else if (lpPoints[i].y > y1)
41
y1 = lpPoints[i].y;
42
}
43
if (y0 >= y1)
44
return ;
45
46
// 初始化邊表,第一個元素不用
47
pET = new LPEdge[y1 - y0 + 1];
48
for (i = 0; i <= y1 - y0; i++)
49
{
50
pET[i] = new Edge;
51
pET[i]->pNext = NULL;
52
}
53
54
for (i = 0; i < nCount; i++)
55
{
56
j = (i + 1) % nCount; // 組成邊的下一點
57
if (lpPoints[i].y != lpPoints[j].y)
58
// 如果該邊不是水平的則加入邊表
59
{
60
LPEdge peg; // 指向該邊的指針
61
LPEdge ppeg; // 指向邊指針的指針
62
63
// 構造邊
64
peg = new Edge;
65
k = (lpPoints[i].y > lpPoints[j].y) ? i : j;
66
peg->ymax = lpPoints[k].y; // 該邊最大y坐標
67
k = (k == j) ? i : j;
68
peg->x = (float)lpPoints[k].x; // 該邊與掃描線焦點x坐標
69
if (lpPoints[i].y != lpPoints[j].y)
70
peg->dx = (float)(lpPoints[i].x - lpPoints[j].x) / (lpPoints[i].y -
71
lpPoints[j].y);
72
// 該邊斜率的倒數
73
peg->pNext = NULL;
74
75
// 插入邊
76
ppeg = pET[lpPoints[k].y - y0];
77
while (ppeg->pNext)
78
ppeg = ppeg->pNext;
79
ppeg->pNext = peg;
80
} // end if
81
} // end for i
82
83
// 掃描
84
for (i = y0; i <= y1; i++)
85
{
86
LPEdge peg0 = pET[i - y0]->pNext;
87
LPEdge peg1 = pET[i - y0];
88
if (peg0)
89
// 有新邊加入
90
{
91
while (peg1->pNext)
92
peg1 = peg1->pNext;
93
peg1->pNext = pAET->pNext;
94
pAET->pNext = peg0;
95
}
96
97
// 按照x遞增排序pAET
98
peg0 = pAET;
99
while (peg0->pNext)
100
{
101
LPEdge pegmax = peg0;
102
LPEdge peg1 = peg0;
103
LPEdge pegi = NULL;
104
105
while (peg1->pNext)
106
{
107
if (peg1->pNext->x > pegmax->pNext->x)
108
pegmax = peg1;
109
peg1 = peg1->pNext;
110
}
111
pegi = pegmax->pNext;
112
pegmax->pNext = pegi->pNext;
113
pegi->pNext = pAET->pNext;
114
pAET->pNext = pegi;
115
if (peg0 == pAET)
116
peg0 = pegi;
117
}
118
119
// 遍歷活邊表,畫線
120
peg0 = pAET;
121
while (peg0->pNext)
122
{
123
if (peg0->pNext->pNext)
124
{
125
Bresenham((int)peg0->pNext->x, i, (int)peg0->pNext->pNext->x, i, DC);
126
peg0 = peg0->pNext->pNext;
127
}
128
else
129
break;
130
}
131
132
// 把ymax=i的節點從活邊表刪除并把每個節點的x值遞增dx
133
peg0 = pAET;
134
while (peg0->pNext)
135
{
136
if (peg0->pNext->ymax < i + 2)
137
{
138
peg1 = peg0->pNext;
139
peg0->pNext = peg0->pNext->pNext; //刪除
140
delete peg1;
141
continue;
142
}
143
peg0->pNext->x += peg0->pNext->dx; //把每個節點的x值遞增dx
144
peg0 = peg0->pNext;
145
}
146
}
147
148
// 刪除邊表
149
for (i = 0; i < y1 - y0; i++)
150
if (pET[i])
151
delete pET[i];
152
153
if (pAET)
154
delete pAET;
155
if (pET)
156
delete []pET;
157
}

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

148

149

150

151

152

153

154

155

156

157

分析:
以下是被修改后的WM_TIMER消息代碼
1
case WM_TIMER:
2
GetClientRect (hwnd, &rect) ;
3
if(rect.right <=0 || rect.bottom<=0) return 0; //窗口最小化后結束繪制
4
hdc = GetDC (hwnd) ;
5
hdcMem = CreateCompatibleDC(NULL); //創建內存設備環境
6
hBitmap = CreateCompatibleBitmap(hdc,
7
rect.right, rect.bottom); //創建內存設備環境相關的位圖
8
SelectObject(hdcMem, hBitmap); //選擇位圖對象到內存設備環境
9
10
for(int i=0;i<12;i++) // 循環次數必須是偶數,否則最后一根線無法形成閉合區域
11
{
12
// COLORREF crColor = RGB(rand()%256,rand()%256,rand()%256); //隨機產生點的顏色值
13
// SetPixel (hdc, x, y, crColor) ; //在顯示設備環境中繪制點
14
// SetPixel (hdcMem, x, y, crColor) ;//在內存設備環境中繪制點
15
16
static int xTemp = -1;
17
static int yTemp = -1;
18
19
x = rand()%rect.right; //隨機產生點的X坐標
20
y = rand()%rect.bottom; //隨機產生點的Y坐標
21
22
Bresenham(rect.right/2,rect.bottom/2,x,y,hdcMem);
23
24
if(xTemp != -1 && yTemp != -1)
25
{
26
Bresenham(x,y,xTemp,yTemp,hdcMem);
27
28
POINT pts[3]; //填充區域的3個頂點
29
pts[0].x = rect.right/2;
30
pts[0].y = rect.bottom/2;
31
pts[1].x = x;
32
pts[1].y = y;
33
pts[2].x = xTemp;
34
pts[2].y = yTemp;
35
FillPolygon(pts , 3 , 0 ,hdcMem);
36
37
xTemp = -1;
38
yTemp = -1;
39
40
}else
41
{
42
xTemp = x;
43
yTemp = y;
44
}
45
}
46
47
BitBlt(hdc,0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY); //將內存設備環境中的數據傳到顯示設備環境顯示
48
DeleteObject(hBitmap); //釋放位圖對象
49
DeleteDC (hdcMem) ; //釋放內存設備環境
50
ReleaseDC (hwnd, hdc) ; //釋放顯示設備環境
51
return 0 ;

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
