跟我一起学图形编程
作者:姚明 联系方式:alanvincentmail@gmail.com 2011年1月25日 21:16:15
从本课开始,我们才真正接触到图形学的相关算法,前面教程只是在搭建环境,从这节课开始,我们可以把精力集中在算法上,再不需要了解太多的系统函数。上节课,我们感受到了“点”的魅力,要知道,世界上所有的画面都是由点组成的,包括我们上节课例子程序中随机生成的图像,从某种意义上说,那些图像每时每刻都是一副独一无二的画,但是,为什么我们不觉得它是真正的画呢?那是因为,它的每个像素都是随机产生的,像素与像素之间没有规律,没有联系,所以,我们也无法从中获取信息,换句话说,那些都是不包含任何信息的画面。现在,我们试图让象素和象素之间产生关系,其中最常见的一种就是直线。有了它,我们就可以在画面中表达信息了。
我不打算具体的描述算法细节,因为有很多书,都写得很详细,易懂,甚至配有动画效果,例如:点击下载。每个人的时间和精力有限,不能把宝贵的时间用在重复的发明轮子上,另外,把饭端到嘴边,再用勺子喂饭的事情,那是一种失败。我更愿意充当一名向导的角色,指引着大家如何学?怎么学?学什么?同时激起大家的兴趣和想象力,跟着我共同提高。
理论:
数学告诉我们连续和离散的概念,在计算机中,我们接触到的往往是离散的事物,例如,我们现在看到的显示屏,就是由离散的象素点,排列组成的。每个像素都用X和Y两个整数表达位置。现在问题出现了,我们画的线是个连续量,所以,有的象素X和Y的位置不一定是整数,有可能产生小数,出现小数时,我们必须取整才能和屏幕上的象素对应,这个过程就有精度的缺失,所以我们屏幕上得到的结果是离散后的近似值。DDA算法是用微分方程得到斜率k,注意k是小数而且用除法算出来的,所以,计算机中运算效率不高。要知道,直线是组成任何画面的基础元素,在直线算法中,效率提高1分,有可能让整个场景效率提高100分,因此,DDA很快就被其它算法取代,其中Bresenham算法比较优秀,它用一个判别式做决策,决定下一点的位置,判别式中没有小数运算,虽然,有乘2运算,但乘2可以用移位运算代替,所以效率极高。
内容:
1
/**//*------------------------------------------------------------------------
2
LINES.CPP – 在窗口客户区绘制直线的动画效果
3
4
(c) 姚明, 2010
5
-----------------------------------------------------------------------*/
6
#include <windows.h>
7
#include <math.h>
8
9
#define ID_TIMER 1
10
11
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
12
BOOL Bresenham(int nX1, int nY1, int nX2, int nY2, HDC &DC);
13
14
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
15
PSTR szCmdLine, int iCmdShow)
16

{
17
static TCHAR szAppName[] = TEXT ("lines") ;
18
HWND hwnd ;
19
MSG msg ;
20
WNDCLASS wndclass ;
21
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
22
wndclass.lpfnWndProc = WndProc ;
23
wndclass.cbClsExtra = 0 ;
24
wndclass.cbWndExtra = 0 ;
25
wndclass.hInstance = hInstance ;
26
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
27
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
28
wndclass.hbrBackground= (HBRUSH) GetStockObject (BLACK_BRUSH) ;
29
wndclass.lpszMenuName = NULL ;
30
wndclass.lpszClassName= szAppName ;
31
RegisterClass (&wndclass);
32
hwnd = CreateWindow( szAppName, // window class name
33
TEXT ("draw lines"), // window caption
34
WS_OVERLAPPEDWINDOW, // window style
35
CW_USEDEFAULT, // initial x position
36
CW_USEDEFAULT, // initial y position
37
CW_USEDEFAULT, // initial x size
38
CW_USEDEFAULT, // initial y size
39
NULL, // parent window handle
40
NULL, // window menu handle
41
hInstance, // program instance handle
42
NULL) ; // creation parameters
43
ShowWindow (hwnd, iCmdShow) ;
44
UpdateWindow (hwnd) ;
45
while (GetMessage (&msg, NULL, 0, 0))
46
{
47
TranslateMessage (&msg) ;
48
DispatchMessage (&msg) ;
49
}
50
return msg.wParam ;
51
}
52
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
53

{
54
HDC hdc ;
55
HDC hdcMem; //内存设备句柄
56
PAINTSTRUCT ps ;
57
RECT rect ;
58
int x,y;
59
HBITMAP hBitmap;
60
61
switch (message)
62
{
63
case WM_CREATE:
64
SetTimer (hwnd, ID_TIMER, 100, NULL) ; //创建定时器,每10微妙产生一个WM_TIMER消息
65
return 0 ;
66
67
case WM_TIMER:
68
GetClientRect (hwnd, &rect) ;
69
if(rect.right <=0 || rect.bottom<=0) return 0; //窗口最小化后结束绘制
70
hdc = GetDC (hwnd) ;
71
hdcMem = CreateCompatibleDC(NULL); //创建内存设备环境
72
hBitmap = CreateCompatibleBitmap(hdc,
73
rect.right, rect.bottom); //创建内存设备环境相关的位图
74
SelectObject(hdcMem, hBitmap); //选择位图对象到内存设备环境
75
76
for(int i=0;i<100;i++)
77
{
78
// COLORREF crColor = RGB(rand()%256,rand()%256,rand()%256); //随机产生点的颜色值
79
// SetPixel (hdc, x, y, crColor) ; //在显示设备环境中绘制点
80
// SetPixel (hdcMem, x, y, crColor) ;//在内存设备环境中绘制点
81
82
x = rand()%rect.right; //随机产生点的X坐标
83
y = rand()%rect.bottom; //随机产生点的Y坐标
84
85
Bresenham(rect.right/2,rect.bottom/2,x,y,hdcMem);
86
}
87
88
BitBlt(hdc,0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY); //将内存设备环境中的数据传到显示设备环境显示
89
DeleteObject(hBitmap); //释放位图对象
90
DeleteDC (hdcMem) ; //释放内存设备环境
91
ReleaseDC (hwnd, hdc) ; //释放显示设备环境
92
return 0 ;
93
case WM_DESTROY:
94
KillTimer (hwnd, ID_TIMER) ; //销毁定时器
95
PostQuitMessage (0) ;
96
return 0 ;
97
}
98
return DefWindowProc (hwnd, message, wParam, lParam) ;
99
}
100
101
//交换两个整形变量
102
void SwapInt(int &nTempA, int &nTempB)
103

{
104
int nTemp = nTempA;
105
nTempA = nTempB;
106
nTempB = nTemp;
107
}
108
BOOL Bresenham(int nX1, int nY1, int nX2, int nY2, HDC &DC)
109

{
110
int nDx = abs(nX2 - nX1);
111
int nDy = abs(nY2 - nY1);
112
bool bYDirection = false;
113
114
if (nDx < nDy)
115
{
116
// y direction is step direction
117
SwapInt(nX1, nY1);
118
SwapInt(nDx, nDy);
119
SwapInt(nX2, nY2);
120
bYDirection = true;
121
}
122
123
// calculate the x, y increment
124
int nIncreX = (nX2 - nX1) > 0 ? 1 : -1;
125
int nIncreY = (nY2 - nY1) > 0 ? 1 : -1;
126
127
int nCurX = nX1;
128
int nCurY = nY1;
129
int nTwoDY = 2 * nDy;
130
int nTwoDyDx = 2 * (nDy - nDx);
131
int nIniD = 2 * nDy - nDx;
132
133
COLORREF crColor = RGB(rand()%256,rand()%256,rand()%256); //随机产生点的颜色值
134
135
while (nCurX != nX2) // nCurX == nX2 can not use in bitmap
136
{
137
if(nIniD < 0)
138
{
139
nIniD += nTwoDY;
140
// y value keep current state
141
}
142
else
143
{
144
nCurY += nIncreY;
145
nIniD += nTwoDyDx;
146
}
147
148
if (bYDirection)
149
{
150
SetPixel(DC, nCurY, nCurX, crColor);
151
}
152
else
153
{
154
SetPixel(DC, nCurX, nCurY, crColor);
155
}
156
nCurX += nIncreX;
157
}
158
return TRUE;
159
}
分析:
1
//交换两个整形变量
2
void SwapInt(int &nTempA, int &nTempB)
3

{
4
int nTemp = nTempA;
5
nTempA = nTempB;
6
nTempB = nTemp;
7
}
最典型的两个数据交换的代码,A,B通过临时变量C交换数据值。能不能不用临时变量C,就能交换两个数据值的方法呢?答案是肯定的。看下面
1
//交换两个整形变量
2
void SwapInt(int &nTempA, int &nTempB)
3

{
4
If(nTempA == nTempB) return;
5
nTempA = nTempA ^ nTempB;
6
nTempB = nTempA ^ nTempB;
7
nTempA = nTempA ^ nTempB;
8
}
接下来是Bresenham算法的具体实现:
BOOL Bresenham(int nX1, int nY1, int nX2, int nY2, HDC &DC)
nX1,nY2是起始点坐标,nX2,nY2是终点坐标,DC是绘制设备环境
{
int nDx = abs(nX2 - nX1);
计算△X,abs是取绝对值函数
int nDy = abs(nY2 - nY1);
计算△Y
bool bYDirection = false;
初始化步进方向为X
if (nDx < nDy)
{
如果斜率大于1,设置Y为步进方向,并交换起始点和终点的X,Y值和△X,△Y值
SwapInt(nX1, nY1);
SwapInt(nDx, nDy);
SwapInt(nX2, nY2);
bYDirection = true;
}
int nIncreX = (nX2 - nX1) > 0 ? 1 : -1;
int nIncreY = (nY2 - nY1) > 0 ? 1 : -1;
计算X和Y方向的增量
int nCurX = nX1;
int nCurY = nY1;
设置起点值
int nTwoDY = 2 * nDy;
int nTwoDyDx = 2 * (nDy - nDx);
int nIniD = 2 * nDy - nDx;
计算步进决策判别式初始值
COLORREF crColor = RGB(rand()%256,rand()%256,rand()%256);
随机产生点的颜色值
while (nCurX != nX2)
{
开始循坏绘制象素
if(nIniD < 0)
{
判断结果为负数
nIniD += nTwoDY;
Y值保持不变
}
else
{
否则,判断结果为正数
nCurY += nIncreY;
Y值改变
nIniD += nTwoDyDx;
}
if (bYDirection)
{
如果Y是步进方向
SetPixel(DC, nCurY, nCurX, crColor);
交换X,Y位置绘制象素
}
else
{
否则,按正常绘制象素
SetPixel(DC, nCurX, nCurY, crColor);
}
nCurX += nIncreX;
}
return TRUE;
}
运行演示程序时候,注意仔细观察斜率偏低或偏高时产生的锯齿现象。
posted on 2011-01-25 21:02
姚明 阅读(515)
评论(0) 编辑 收藏 引用 所属分类:
原创教程