Windows游戏编程大师技巧之三角形填充

一、 三角形的种类

三角形一般可以分为如下的四种类型(这四种类型是对于计算机来说的,不是数学意义上的分类):
平顶三角形:就是在计算机中显示的上面两个顶点的Y坐标相同。
平底三角形:就是在计算机中显示的时候下面两个顶点的Y坐标相同。
右边为主三角形:这种三角形三个点的Y坐标都不相同,但是右边的一条边是最长的斜边
左边为主的三角形:这种三角形的三个点的Y坐标不相同,但是左边的一条边是最长的斜边。
附上各个不同三角形的图:

是不是所有的三角形大致可以分为这四种?而我们要进行填充的时候,前面两种的填充应该比较简单,只要从顶点开始一行一行的进行填充就可以做到了,如下图所示:

进行这样的操作应该很容易实现吧,同样的对平顶的三角形我们也可以这样来一行一行的使用像素进行填充。
至于后面两种情况的三角形,我们能够很简单的将他们分别变成两个三角形,一个平顶一个平底:

所以,我们实现了前面两个的三角形的填充,后面两个的也就很容易实现了。

二、代码实现

下面是实现平顶的三角形填充的代码:
    int Draw_Top_Trangle(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch)  
    {  
        //先判断下输入的三角形  
        if(y0==y1)  
        {  
        }else if(y0==y2)  
        {  
            Swap(x2,x1);  
            Swap(y2,y1);  
        }else if(y1==y2)  
        {  
            Swap(x0,x2);  
            Swap(y0,y2);  
        }else  
        {  
            return 1 ; //error \brief 不是平顶三角形  
        }  
      
        if(x1<x0)  
        {  
            Swap(x1,x0);  
            Swap(y1,y0);  
        }  
        else if(x1 == x0)  
        {  
            return 1 ;// error \brief不是三角形  
        }  
      
        //计算左右误差  
        float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
        float dxy_right = (x1-x2)*1.0/(y1-y2);  
      
        //开始进行填充  
        float xs = x0 ,xe = x1 ;  
        for(int y=y0 ; y <=y2 ;y++)  
        {  
            Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
      
            xs += dxy_left ;  
            xe += dxy_right ;  
        }  
    } // end Draw_Top_Trangle  



上面的算法开始的时候,检查下输入的三个点是否能够构成三角形,并且按照下面图中坐标点所示,来进行顺序的重新排列:

因为用户使用的时候,可能传递的三个点不是上图中所示的那样的顺序,所以我们计算的方便,我们先将这三个点转变成上图中相对应的位置。接下来就是计算在Y方向上,每移动一个像素,左边和右边的直线上X的平均该变量是多少。获得了这个值,我们就可以慢慢的向下迭代下去,从而将三角形进行了填充。当然,你也可以使用其他的方法来。
(注:上面函数中的UINT*video_buffer,和int mempitch对于学习过DirectDraw的读者应该比较熟悉,分别是表面内存数据,和内存跨度,此函数是DirectDraw的实现版本)

同样的,来看看我们的平底三角形填充的实现:
    int Draw_Bottom_Trangle(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch)  
    {  
        //先判断下输入的三角形  
        if(y2==y1)  
        {  
        }else if(y2==y0)  
        {  
            Swap(x0,x1);  
            Swap(y0,y1);  
        }else if(y0==y1)  
        {  
            Swap(x0,x2);  
            Swap(y0,y2);  
        }else  
        {  
            return 1 ; //error \brief 不是平顶三角形  
        }  
      
        if(x1<x2)  
        {  
            Swap(x1,x2);  
        }  
        else if(x1 == x2)  
        {  
            return 1 ;// error \brief不是三角形  
        }  
      
        //计算左右误差  
        float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
        float dxy_right = (x1-x0)*1.0/(y1-y0);  
      
        //开始进行填充  
        float xs = x0 ,xe = x0 ;  
        for(int y=y0 ; y <=y2 ;y++)  
        {  
            Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
      
            xs += dxy_left ;  
            xe += dxy_right ;  
        }  
    }// end Draw_Bottom_Trangle  
和上面平顶的算法基本上一致,只有图中点的顺序不同:

好了,这两个函数都实现了,接下来看看我们任意的三角形绘制的实现吧:
    int Draw_Trangle_2D(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch)  
    {  
        if((x0==x1&&x1==x2)  
            ||(y0==y1&&y1==y2))  
        {  
            return 1 ; //error \brief传进来的点无法构成三角形  
        }  
      
        //\brief 将三个顶点按照从上到下排序  
        if(y0>y1)  
        {  
            Swap(x0,x1);  
            Swap(y0,y1);  
        }  
      
        if(y0>y2)  
        {  
            Swap(x0,x2);  
            Swap(y0,y2);  
        }  
      
        if(y1>y2)  
        {  
            Swap(y1,y2);  
            Swap(x1,x2);  
        }  
      
        //\brief查找最大的x坐标,和最小的y坐标  
        int min = (x0<x1?x0:x1);  
        min = (min<x2?min:x2);  
        int max = (x0>x1?x0:x1);  
        max = (max>x2?max:x2);  
      
        //\brief 进行绘制  
        if(y2<=min_clip_y||y0>=max_clip_y  
            ||min>=max_clip_x||max<=min_clip_x)  
            return 1 ;  //\brief 全部在裁剪区之外  
        if(y0 == y1) //\brief 平顶三角形  
        {  
            Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
        }else if(y1 == y2)  
        {  
            Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
        }else  
        {  
            int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0);  
            Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch);  
            Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch);  
        }  
      
      
        return 0 ; //\brief 成功画出三角形  
    }// end Draw_Trangle_2D  



这个函数,先将输入的三个点按照y坐标从小到大排序,这样我们就可以y1的坐标,来寻找分离一个右边为主或者左边为主的三角形成为一个平顶一个平底的三角形了。(由于排序了,所以y1的坐标就是显示屏幕上从上到下中间的那个点了,想象是不是这样的!?)。分离了之后,我们就可以分别调用绘制平底和平顶的三角形的算法来实现了。

以下是整个工程的完整代码:
    // DEMO8_8.CPP 此Demo演示32位窗口模式下,创建任意填充三角形的算法  
      
    // INCLUDES ///  
      
    #define WIN32_LEAN_AND_MEAN  // just say no to MFC  
      
    #define INITGUID // make sure directX guids are included  
      
    #include <windows.h>   // include important windows stuff  
    #include <windowsx.h>   
    #include <mmsystem.h>  
    #include <iostream> // include important C/C++ stuff  
    using namespace std ;  
    #include <conio.h>  
    #include <stdlib.h>  
    #include <malloc.h>  
    #include <memory.h>  
    #include <string.h>  
    #include <stdarg.h>  
    #include <stdio.h>   
    #include <math.h>  
    #include <io.h>  
    #include <fcntl.h>  
      
    #include <ddraw.h> // include directdraw  
    #pragma comment(lib,"ddraw.lib")  
    // DEFINES   
      
    // defines for windows   
    #define WINDOW_CLASS_NAME L"WINCLASS1"  
      
    // default screen size  
    #define SCREEN_WIDTH    640  // size of screen  
    #define SCREEN_HEIGHT   480  
    #define SCREEN_BPP      32   // bits per pixel  
    #define MAX_COLORS      256  // maximum colors  
      
    // TYPES //  
      
    // basic unsigned types  
    typedef unsigned short USHORT;  
    typedef unsigned short WORD;  
    typedef unsigned char  UCHAR;  
    typedef unsigned char  BYTE;  
      
    // MACROS /  
      
    #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)  
    #define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)  
      
    // initializes a direct draw struct  
    #define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }  
      
    //initializes a RGB value  
    #define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))  
    #define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))  
      
    // GLOBALS   
    HWND      main_window_handle = NULL; // globally track main window  
    HINSTANCE hinstance_app      = NULL; // globally track hinstance  
      
    // directdraw stuff  
      
    LPDIRECTDRAW7         lpdd         = NULL;   // dd object  
    LPDIRECTDRAWSURFACE7  lpddsprimary = NULL;   // dd primary surface  
    LPDIRECTDRAWSURFACE7  lpddsback    = NULL;   // dd back surface  
    LPDIRECTDRAWPALETTE   lpddpal      = NULL;   // a pointer to the created dd palette  
    LPDIRECTDRAWCLIPPER   lpddclipper  = NULL;   // dd clipper  
    PALETTEENTRY          palette[256];          // color palette  
    PALETTEENTRY          save_palette[256];     // used to save palettes  
    DDSURFACEDESC2        ddsd;                  // a direct draw surface description struct  
    DDBLTFX               ddbltfx;               // used to fill  
    DDSCAPS2              ddscaps;               // a direct draw surface capabilities struct  
    HRESULT               ddrval;                // result back from dd calls  
    DWORD                 start_clock_count = 0; // used for timing  
    LPDIRECTDRAWSURFACE7  lpddsOffScreen = NULL ;  //离屏表面  
    int                   window_close  =  0 ;    //标识窗口是否关闭  
      
    // these defined the general clipping rectangle  
    int min_clip_x = 0,                          // clipping rectangle   
        max_clip_x = 1366-1,  
        min_clip_y = 0,  
        max_clip_y = 768-1;  
      
    // these are overwritten globally by DD_Init()  
    int screen_width  = SCREEN_WIDTH,            // width of screen  
        screen_height = SCREEN_HEIGHT,           // height of screen  
        screen_bpp    = SCREEN_BPP;              // bits per pixel  
      
      
    char buffer[80];                     // general printing buffer  
      
    //申明画线方法  
    int Draw_Line(int x0, int y0, int x1, int y1, DWORD color , UINT * video_buffer , int stepx , int stepy);  
      
    //裁剪直线算法  
    int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height);  
      
    //交换值  
    void Swap(int &x , int &y) ;  
      
    //绘制填充平顶三角形  
    int Draw_Top_Trangle(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch);  
      
    //绘制平底三角形  
    int Draw_Bottom_Trangle(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch);  
      
    //简单的平行绘制直线  
    int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch);  
      
    //绘制任意三角形  
    int Draw_Trangle_2D(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch);  
    // FUNCTIONS //  
    LRESULT CALLBACK WindowProc(HWND hwnd,   
                                UINT msg,   
                                WPARAM wparam,   
                                LPARAM lparam)  
    {  
    // this is the main message handler of the system  
    PAINTSTRUCT     ps;     // used in WM_PAINT  
    HDC             hdc;    // handle to a device context  
    char buffer[80];        // used to print strings  
      
    // what is the message   
    switch(msg)  
        {     
        case WM_CREATE:   
            {  
            // do initialization stuff here  
            // return success  
            return(0);  
            } break;  
         
        case WM_PAINT:   
            {  
            // simply validate the window   
            hdc = BeginPaint(hwnd,&ps);    
              
            // end painting  
            EndPaint(hwnd,&ps);  
      
            // return success  
            return(0);  
            } break;  
      
        case WM_DESTROY:   
            {  
      
            // kill the application, this sends a WM_QUIT message   
            PostQuitMessage(0);  
      
            // return success  
            return(0);  
            } break;  
      
        default:break;  
      
        } // end switch  
      
    // process any messages that we didn't take care of   
    return (DefWindowProc(hwnd, msg, wparam, lparam));  
      
    } // end WinProc  
      
    ///  
      
    //程序主循环  
    int Game_Main(void *parms = NULL, int num_parms = 0)  
    {  
    // this is the main loop of the game, do all your processing  
    // here  
      
    // for now test if user is hitting ESC and send WM_CLOSE  
    if(window_close)  
        return 1 ;  
    if (KEYDOWN(VK_ESCAPE))  
    {  
        PostMessage(main_window_handle,WM_CLOSE,0,0);  
        window_close = 1 ;  
    }  
         
      
    //清空表面  
    DDBLTFX bltfx ;  
    DD_INIT_STRUCT(bltfx);  
    bltfx.dwFillColor = 0 ;  
    if(FAILED(lpddsOffScreen->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx)))  
    {  
        OutputDebugString(L"OffScreen Blt error");  
        return 1 ;  
    }  
      
    //锁定  
    DDSURFACEDESC2 ddsd ;  
    DD_INIT_STRUCT(ddsd);  
    if(FAILED(lpddsOffScreen->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)))  
    {  
        OutputDebugString(L"Lock error");  
        return 1 ;  
    }  
      
    //获取窗口位置  
    RECT rect ;  
    GetWindowRect(main_window_handle,&rect);  
    //画填充的三角形  
    int x0 = rand()%SCREEN_WIDTH+rect.left;  
    int x1 = rand()%SCREEN_WIDTH+rect.left ;  
    int x2 = rand()%SCREEN_WIDTH + rect.left ;  
    int y0 = rand()%SCREEN_HEIGHT + rect.top;  
    int y1 = rand()%SCREEN_HEIGHT+ rect.top+100;  
    int y2 = rand()%SCREEN_HEIGHT + rect.top;  
    Draw_Trangle_2D(x0,y0,x1,y1,x2,y2,(UINT*)ddsd.lpSurface,  
        _RGB32BIT(0,255,255,255),ddsd.lPitch>>2);  
      
    //解锁  
    if(FAILED(lpddsOffScreen->Unlock(NULL)))  
    {  
        OutputDebugString(L"Unlock error");  
        return 1 ;  
    }  
      
    //Blt到主表面  
    if(FAILED(lpddsprimary->Blt(NULL,lpddsOffScreen,NULL,DDBLT_WAIT,NULL)))  
    {  
        OutputDebugString(L"Blt error");  
        return 1 ;  
    }  
      
    // return success or failure or your own return code here  
    return(1);  
      
    } // end Game_Main  
      
      
      
    int Game_Init(void *parms = NULL, int num_parms = 0)  
    {  
    // this is called once after the initial window is created and  
    // before the main event loop is entered, do all your initialization  
    // here  
      
    // create IDirectDraw interface 7.0 object and test for error  
    if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))  
       return(0);  
      
    // set cooperation to normal since this will be a windowed app  
    if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_NORMAL)))  
    {  
        MessageBox(NULL,L"SetCooperativeLevel error",L"error",MB_OK);  
        return 0 ;  
    }  
      
    //创建裁剪器  
    if(FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))  
    {  
        OutputDebugString(L"CreateClipper error");  
        return 1 ;  
    }  
      
    //将裁减器关联窗口,也就是用窗口的尺寸作为裁剪器的裁剪序列  
    if(FAILED(lpddclipper->SetHWnd(0,main_window_handle)))  
    {  
        OutputDebugString(L"SetHWnd error");  
        return 1 ;  
    }  
      
    //创建主表面  
    memset(&ddsd,0,sizeof(ddsd));  
    ddsd.dwSize = sizeof(ddsd);  
    ddsd.dwFlags = DDSD_CAPS ;  
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;  
      
    if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)))  
    {  
        MessageBox(NULL,L"CreateSurface error",L"error",MB_OK);  
        return 0 ;  
    }  
      
    //将裁减器关联到表面  
    if(FAILED(lpddsprimary->SetClipper(lpddclipper)))  
    {  
        OutputDebugString(L"SetClipper error");  
        return 1 ;  
    }  
      
    //创建一个离屏表面  
    DD_INIT_STRUCT(ddsd);  
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;  
    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;  
    ddsd.dwHeight = 786 ;  
    ddsd.dwWidth = 1366 ;  
    if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsOffScreen,NULL)))  
    {  
        OutputDebugString(L"OffScreen CreateSurface error");  
        return 1 ;  
    }  
      
      
      
    // return success or failure or your own return code here  
    return(1);  
      
    } // end Game_Init  
      
    /  
      
    int Game_Shutdown(void *parms = NULL, int num_parms = 0)  
    {  
    // this is called after the game is exited and the main event  
    // loop while is exited, do all you cleanup and shutdown here  
      
    // simply blow away the IDirectDraw4 interface  
      
    if(lpddclipper)  
    {  
        lpddclipper->Release();  
        lpddclipper = NULL ;  
    }  
      
    if(lpddsprimary)  
    {  
        lpddsprimary->Release();  
        lpddsprimary = NULL ;  
    }  
      
    if (lpdd)  
       {  
       lpdd->Release();  
       lpdd = NULL;  
       } // end if  
      
    // return success or failure or your own return code here  
    return(1);  
      
    } // end Game_Shutdown  
      
    // WINMAIN   
    int WINAPI WinMain( HINSTANCE hinstance,  
                        HINSTANCE hprevinstance,  
                        LPSTR lpcmdline,  
                        int ncmdshow)  
    {  
      
        WNDCLASSEX winclass; // this will hold the class we create  
        HWND       hwnd;     // generic window handle  
        MSG        msg;      // generic message  
        HDC        hdc;      // graphics device context  
      
        // first fill in the window class stucture  
        winclass.cbSize         = sizeof(WNDCLASSEX);  
        winclass.style          = CS_DBLCLKS | CS_OWNDC |   
                                  CS_HREDRAW | CS_VREDRAW;  
        winclass.lpfnWndProc    = WindowProc;  
        winclass.cbClsExtra     = 0;  
        winclass.cbWndExtra     = 0;  
        winclass.hInstance      = hinstance;  
        winclass.hIcon          = LoadIcon(NULL, IDI_APPLICATION);  
        winclass.hCursor        = LoadCursor(NULL, IDC_ARROW);   
        winclass.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);  
        winclass.lpszMenuName   = NULL;  
        winclass.lpszClassName  = WINDOW_CLASS_NAME;  
        winclass.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);  
      
        // save hinstance in global  
        hinstance_app = hinstance;  
      
        // register the window class  
        if (!RegisterClassEx(&winclass))  
            return(0);  
      
        // create the window  
        if (!(hwnd = CreateWindowEx(NULL,                  // extended style  
                                    WINDOW_CLASS_NAME,     // class  
                                    L"DirectDraw Initialization Demo", // title  
                                    WS_OVERLAPPED|WS_VISIBLE,  
                                    0,0,      // initial x,y  
                                    SCREEN_WIDTH,SCREEN_HEIGHT,  // initial width, height  
                                    NULL,     // handle to parent   
                                    NULL,     // handle to menu  
                                    hinstance,// instance of this application  
                                    NULL))) // extra creation parms  
            return(0);  
      
        // save main window handle  
        main_window_handle = hwnd;  
      
        // initialize game here  
        Game_Init();  
      
        //调整窗口大小  
        RECT window_rect = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT} ;  
        AdjustWindowRectEx(&window_rect,GetWindowStyle(main_window_handle),GetMenu(main_window_handle)!=NULL,GetWindowExStyle(main_window_handle));  
      
        // enter main event loop  
        while(TRUE)  
            {  
            // test if there is a message in queue, if so get it  
            if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))  
               {   
               // test if this is a quit  
               if (msg.message == WM_QUIT)  
                   break;  
          
               // translate any accelerator keys  
               TranslateMessage(&msg);  
      
               // send the message to the window proc  
               DispatchMessage(&msg);  
               } // end if  
          
               // main game processing goes here  
               Game_Main();  
             
            } // end while  
      
        // closedown game here  
        Game_Shutdown();  
      
        // return to Windows like this  
        return(msg.wParam);  
      
    } // end WinMain  
      
    //定义交换函数  
    void Swap(int &x , int &y)  
    {  
        int temp = y ;  
        y = x ;  
        x = temp ;  
    }  
      
    //定义画线函数  
    int Draw_Line(int x0,int y0, int x1, int y1 , DWORD color , UINT *video_buffer, int stepx,int stepy)  
    {  
        int dx ,  //起点与终点的X方向间距  
            dy ,  //起点与终点的Y方向间距  
            dx2, //两倍的dx  
            dy2,  //两倍的dy  
            x_inc ,  //实际的x步长值,带有符号  
            y_inc , //实际的y步长值,带有符号  
            p ;     //误差项  
      
        dx = x1 - x0 ;  //计算x间距  
        dy = y1 - y0 ;  //计算y间距  
      
        //计算起点的缓冲地址  
        video_buffer+=x0+y0*stepy ;  
      
        //确定x方向的步进值  
        if(dx>=0)  
        {  
            x_inc = stepx;  
        }  
        else  
        {  
            x_inc = -stepx ;  
            dx = -dx ;  
        }  
      
        //确定y方向的步进值  
        if(dy>=0)  
        {  
            y_inc = stepy ;  
        }  
        else  
        {  
            y_inc = -stepy ;  
            dy = -dy ;  
        }  
      
        //确定dx2,dy2的值  
        dx2 = dx<<1;  
        dy2 = dy<<1 ;  
      
        //进行步进的选择  
        if(dx <= dy) //斜率绝对值大于1  
        {  
            Swap(dx,dy);  
            Swap(x_inc,y_inc);  
            Swap(dx2,dy2);  
        }  
        else //斜率绝对值小于1,不需要交换  
        {  
        }  
      
      
        //绘制直线  
        p = dy2 - dx ;  //计算起点的误差值  
        for(int i = 0 ; i < dx ; i++)  
        {  
            *video_buffer = color ;  
              
            video_buffer += x_inc ;  
            if(p>=0)  
            {  
                video_buffer += y_inc ;  
                p = p + dy2 - dx2 ;  
            }  
            else  
            {  
                p = p + dy2 ;  
            }  
        }// end for  
      
      
        return 0 ;  
    }// end Draw_Line  
      
    //定义裁剪直线算法  
    int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height)  
    {  
    #define CLIP_CODE_C 0x0000  
    #define CLIP_CODE_N 0x0008  
    #define CLIP_CODE_S 0x0004  
    #define CLIP_CODE_E 0x0002  
    #define CLIP_CODE_W 0x0001  
      
    #define CLIP_CODE_NE 0x000a  
    #define CLIP_CODE_SE 0x0006  
    #define CLIP_CODE_NW 0x0009  
    #define CLIP_CODE_SW 0x0005  
        int xc0 = x0 ,yc0 = y0 , xc1=x1 , yc1=y1 ;  
        int min_clip_x = SCREEN_WIDTH/3 ,min_clip_y = SCREEN_HEIGHT/3 ,max_clip_x = screen_width*2/3-1,max_clip_y=screen_height*2/3-1 ;  
        int p0_code = 0 ,p1_code = 0 ;  
      
        //确定各个顶点所在的位置代码  
        if(y0<min_clip_y)  
            p0_code|=CLIP_CODE_N;  
        else if(y0>max_clip_y)  
            p0_code|=CLIP_CODE_S;  
      
        if(x0<min_clip_x)  
            p0_code|=CLIP_CODE_W;  
        else if(x0>max_clip_x)  
            p0_code|=CLIP_CODE_E;  
      
        if(y1<min_clip_y)  
            p1_code|=CLIP_CODE_N;  
        else if(y1>max_clip_y)  
            p1_code|=CLIP_CODE_S;  
      
        if(x1<min_clip_x)  
            p1_code|=CLIP_CODE_W;  
        else if(x1>max_clip_x)  
            p1_code|=CLIP_CODE_E;  
      
        //先检测一些简单的情况  
        if(p0_code&p1_code) //有相同的位置代码,表示在裁剪区外部  
            return 0 ;  
        if(p0_code==0&&p1_code==0) //表示两个点都在裁剪区内,不需要裁剪  
            return 1 ;  
      
        //判断第一个点的位置代码  
        switch(p0_code)  
        {  
        case CLIP_CODE_C:  
            break;  
        case CLIP_CODE_N:  
            {  
                yc0 = min_clip_y ;  
                xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
                break ;  
            }  
        case CLIP_CODE_S:  
            {  
                yc0 = max_clip_y;  
                xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
                break ;  
            }  
        case CLIP_CODE_W:  
            {  
                xc0=min_clip_x;  
                yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
                break;  
            }  
        case CLIP_CODE_E:  
            {  
                xc0=max_clip_x;  
                yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
                break;  
            }  
        case CLIP_CODE_NE:  
            {  
                yc0 = min_clip_y;  
                xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
      
                if(xc0<min_clip_x||xc0>max_clip_x)  
                {  
                    xc0=max_clip_x;  
                    yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        case CLIP_CODE_SE:  
            {  
                yc0 = max_clip_y;  
                xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
      
                if(xc0<min_clip_x||xc0>max_clip_x)  
                {  
                    xc0=max_clip_x;  
                    yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        case CLIP_CODE_NW:  
            {  
                yc0=min_clip_y;  
                xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
      
                if(xc0<min_clip_x||xc0>max_clip_x)  
                {  
                    xc0=min_clip_x;  
                    yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        case CLIP_CODE_SW:  
            {  
                yc0=max_clip_y;  
                xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
      
                if(xc0<min_clip_x||xc0>max_clip_x)  
                {  
                    xc0=min_clip_x;  
                    yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        default:  
            break;  
        } // end switch(p0_code)  
      
        //判断第二个点的位置代码  
        switch(p1_code)  
        {  
        case CLIP_CODE_C:  
            break;  
        case CLIP_CODE_N:  
            {  
                yc1 = min_clip_y ;  
                xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
                break ;  
            }  
        case CLIP_CODE_S:  
            {  
                yc1 = max_clip_y;  
                xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
                break ;  
            }  
        case CLIP_CODE_W:  
            {  
                xc1=min_clip_x;  
                yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
                break;  
            }  
        case CLIP_CODE_E:  
            {  
                xc1=max_clip_x;  
                yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
                break;  
            }  
        case CLIP_CODE_NE:  
            {  
                yc1 = min_clip_y;  
                xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
      
                if(xc1<min_clip_x||xc1>max_clip_x)  
                {  
                    xc1=max_clip_x;  
                    yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        case CLIP_CODE_SE:  
            {  
                yc1 = max_clip_y;  
                xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
      
                if(xc1<min_clip_x||xc1>max_clip_x)  
                {  
                    xc1=max_clip_x;  
                    yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        case CLIP_CODE_NW:  
            {  
                yc1=min_clip_y;  
                xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
      
                if(xc1<min_clip_x||xc1>max_clip_x)  
                {  
                    xc1=min_clip_x;  
                    yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        case CLIP_CODE_SW:  
            {  
                yc1=max_clip_y;  
                xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
      
                if(xc1<min_clip_x||xc1>max_clip_x)  
                {  
                    xc1=min_clip_x;  
                    yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
                }  
                break;  
            }  
        default:  
            break;  
        } // end switch(p1_code)  
      
        //进行最后的检测  
        if(xc0>max_clip_x||xc0<min_clip_x||  
            yc0>max_clip_y||yc0<min_clip_y||  
            xc1>max_clip_x||xc1<min_clip_x||  
            yc1>max_clip_y||yc1<min_clip_y)  
        {  
            //表示全部在裁剪区外部  
            return 0 ;  
        }  
      
        //将裁减后的数据返回  
        x0 = xc0 ;  
        x1 = xc1 ;  
        y0 = yc0 ;  
        y1 = yc1 ;  
      
        return 1 ;  
    }// end Clipper_Line  
      
    //简单的平行绘制直线  
    int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch)  
    {  
        //进行裁剪  
        if(y<min_clip_y)  
            return 1 ;  
        else if(y>max_clip_y)  
            return 1 ;  
        if(x0<min_clip_x)  
            x0 = min_clip_x;  
        else if(x0>max_clip_x)  
            x0 = max_clip_x ;  
        if(x1<min_clip_x)  
            x1 = min_clip_x ;  
        else if(x1>max_clip_x)  
            x1 = max_clip_x ;  
      
        //进行绘制  
        video_buffer+=y*mempitch;  
        for(int x = x0 ; x<=x1;x++)  
        {  
            video_buffer[x]=color ;  
        }  
    }  
      
    //绘制填充平顶三角形  
    int Draw_Top_Trangle(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch)  
    {  
        //先判断下输入的三角形  
        if(y0==y1)  
        {  
        }else if(y0==y2)  
        {  
            Swap(x2,x1);  
            Swap(y2,y1);  
        }else if(y1==y2)  
        {  
            Swap(x0,x2);  
            Swap(y0,y2);  
        }else  
        {  
            return 1 ; //error \brief 不是平顶三角形  
        }  
      
        if(x1<x0)  
        {  
            Swap(x1,x0);  
            Swap(y1,y0);  
        }  
        else if(x1 == x0)  
        {  
            return 1 ;// error \brief不是三角形  
        }  
      
        //计算左右误差  
        float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
        float dxy_right = (x1-x2)*1.0/(y1-y2);  
      
        //开始进行填充  
        float xs = x0 ,xe = x1 ;  
        for(int y=y0 ; y <=y2 ;y++)  
        {  
            Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
      
            xs += dxy_left ;  
            xe += dxy_right ;  
        }  
    } // end Draw_Top_Trangle  
      
    //绘制平底三角形  
    int Draw_Bottom_Trangle(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch)  
    {  
        //先判断下输入的三角形  
        if(y2==y1)  
        {  
        }else if(y2==y0)  
        {  
            Swap(x0,x1);  
            Swap(y0,y1);  
        }else if(y0==y1)  
        {  
            Swap(x0,x2);  
            Swap(y0,y2);  
        }else  
        {  
            return 1 ; //error \brief 不是平顶三角形  
        }  
      
        if(x1<x2)  
        {  
            Swap(x1,x2);  
        }  
        else if(x1 == x2)  
        {  
            return 1 ;// error \brief不是三角形  
        }  
      
        //计算左右误差  
        float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
        float dxy_right = (x1-x0)*1.0/(y1-y0);  
      
        //开始进行填充  
        float xs = x0 ,xe = x0 ;  
        for(int y=y0 ; y <=y2 ;y++)  
        {  
            Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
      
            xs += dxy_left ;  
            xe += dxy_right ;  
        }  
    }// end Draw_Bottom_Trangle  
      
    //绘制任意三角形  
    int Draw_Trangle_2D(int x0,int y0,  
                        int x1,int y1,  
                        int x2,int y2,  
                        UINT * video_buffer,  
                        DWORD color,int mempitch)  
    {  
        if((x0==x1&&x1==x2)  
            ||(y0==y1&&y1==y2))  
        {  
            return 1 ; //error \brief传进来的点无法构成三角形  
        }  
      
        //\brief 将三个顶点按照从上到下排序  
        if(y0>y1)  
        {  
            Swap(x0,x1);  
            Swap(y0,y1);  
        }  
      
        if(y0>y2)  
        {  
            Swap(x0,x2);  
            Swap(y0,y2);  
        }  
      
        if(y1>y2)  
        {  
            Swap(y1,y2);  
            Swap(x1,x2);  
        }  
      
        //\brief查找最大的x坐标,和最小的y坐标  
        int min = (x0<x1?x0:x1);  
        min = (min<x2?min:x2);  
        int max = (x0>x1?x0:x1);  
        max = (max>x2?max:x2);  
      
        //\brief 进行绘制  
        if(y2<=min_clip_y||y0>=max_clip_y  
            ||min>=max_clip_x||max<=min_clip_x)  
            return 1 ;  //\brief 全部在裁剪区之外  
        if(y0 == y1) //\brief 平顶三角形  
        {  
            Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
        }else if(y1 == y2)  
        {  
            Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
        }else  
        {  
            int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0);  
            Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch);  
            Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch);  
        }  
      
      
        return 0 ; //\brief 成功画出三角形  
    }// end Draw_Trangle_2D  
    ///  


下图是运行结果:

OK,今天就到这里的,明天继续学习
原文地址:http://blog.csdn.net/i_dovelemon/article/details/9131663
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值