姚明,81年,97年开始接触电脑,6年的编程学习经历, 曾有4年工作经验,最终转向基础理论学习和研究, 现华中理工科技大学在读,有志于图形学领域工作发展

EMAIL:alanvincentmail@gmail.com QQ:31547735

随笔分类(34)

文章分类(99)

相册

收藏夹(6)

编程技术网站

出国留学网站

数学资源网站

图形学网站

英语资源网站

自由职业者

搜索

  •  

最新评论

跟我一起学图形编程

                              作者:姚明           联系方式:alanvincentmail@gmail.com     2011年1月25日 21:16:15


从本课开始,我们才真正接触到图形学的相关算法,前面教程只是在搭建环境,从这节课开始,我们可以把精力集中在算法上,再不需要了解太多的系统函数。上节课,我们感受到了“点”的魅力,要知道,世界上所有的画面都是由点组成的,包括我们上节课例子程序中随机生成的图像,从某种意义上说,那些图像每时每刻都是一副独一无二的画,但是,为什么我们不觉得它是真正的画呢?那是因为,它的每个像素都是随机产生的,像素与像素之间没有规律,没有联系,所以,我们也无法从中获取信息,换句话说,那些都是不包含任何信息的画面。现在,我们试图让象素和象素之间产生关系,其中最常见的一种就是直线。有了它,我们就可以在画面中表达信息了。

我不打算具体的描述算法细节,因为有很多书,都写得很详细,易懂,甚至配有动画效果,例如:点击下载。每个人的时间和精力有限,不能把宝贵的时间用在重复的发明轮子上,另外,把饭端到嘴边,再用勺子喂饭的事情,那是一种失败。我更愿意充当一名向导的角色,指引着大家如何学?怎么学?学什么?同时激起大家的兴趣和想象力,跟着我共同提高。

理论:

数学告诉我们连续和离散的概念,在计算机中,我们接触到的往往是离散的事物,例如,我们现在看到的显示屏,就是由离散的象素点,排列组成的。每个像素都用XY两个整数表达位置。现在问题出现了,我们画的线是个连续量,所以,有的象素XY的位置不一定是整数,有可能产生小数,出现小数时,我们必须取整才能和屏幕上的象素对应,这个过程就有精度的缺失,所以我们屏幕上得到的结果是离散后的近似值。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
 11LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
 12BOOL  Bresenham(int nX1, int nY1, int nX2, int nY2, HDC    &DC);
 13 
 14int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
 15                   PSTR szCmdLine, int iCmdShow)
 16{
 17static 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, 00))
 46    {
 47          TranslateMessage (&msg) ;
 48          DispatchMessage (&msg) ;
 49    }

 50    return msg.wParam ;
 51}

 52LRESULT 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<=0return 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,00, rect.right, rect.bottom, hdcMem, 00, 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//交换两个整形变量
102void SwapInt(int &nTempA, int &nTempB)   
103{   
104    int nTemp = nTempA;   
105    nTempA = nTempB;   
106    nTempB = nTemp;   
107}
   
108BOOL  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//交换两个整形变量
2void SwapInt(int &nTempA, int &nTempB)   
3{   
4    int nTemp = nTempA;   
5    nTempA = nTempB;   
6    nTempB = nTemp;   
7}
   

 

最典型的两个数据交换的代码,AB通过临时变量C交换数据值。能不能不用临时变量C,就能交换两个数据值的方法呢?答案是肯定的。看下面

1//交换两个整形变量
2void 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;  

       计算XY方向的增量

    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 姚明 阅读(492) 评论(0)  编辑 收藏 引用 所属分类: 原创教程

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理