天天看點

Linux系統下的OpenGL圖形程式架構            Linux系統下的OpenGL圖形程式架構

            Linux系統下的OpenGL圖形程式架構

        說到圖形界面程式設計,想到的大多是Windows下的多種多樣的應用程式。Linux是一個基于指令行的作業系統,在Linux下的工作大多是在指令行裡完成的,而并非像Windows那樣。圖形界面雖然并不是Linux的一部分,我們通過一些工具,依然可以編寫一些在Linux下的圖形應用程式。

        OpenGL是一個工業标準的三維計算機圖形軟體接口,它由SGI公司釋出并廣泛應用于Unix、OS/2、Windows/NT等多種平台,這其中當然也包括Linux。

        X11也叫做X Window系統,X Window系統 (X11或X)是一種位圖顯示的 視窗系統 。它是在 Unix 和 類Unix作業系統,以及 OpenVMS 上建立圖形使用者界面的标準工具包和協定。Linux就是一個類Unix的作業系統。

        有了這兩個工具,我們便能在Linux下進行圖形界面程式設計了。

1.建立視窗

             由于X Window系統的協定和架構 X基于 用戶端-伺服器 模型,首先建立一個連接配接,連接配接到X伺服器。

dpy = XOpenDisplay(NULL);
    if (dpy == NULL)
        fatalError("could not open display");
           

        對于X視窗系統來說,它所使用的OpenGL擴充是作為OpenGL的一個附件提供的,是以還需要檢測X伺服器是否是支援OpenGL擴充。

if(!glXQueryExtension(dpy, &dummy, &dummy))
        fatalError("X server has no OpenGL GLX extension");
           

  接下來就是給視窗找到一個合适的視覺樣式。比如有些支援雙緩沖,有些不支援,etc。 

vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
    if (vi == NULL)
    {
        vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
        if (vi == NULL) fatalError("no RGB visual with depth buffer");
            glf_DoubleBuffer = GL_FALSE;
    }
    if(vi->class != TrueColor)
        fatalError("TrueColor visual required for this program");
           

  接着建立一個OpenGL狀态資訊,等一下建立視窗會用到。

cx = glXCreateContext(dpy, vi, None, GL_TRUE);
    if (cx == NULL)
        fatalError("could not create rendering context");
           

  調用XCreateWindow函數,使用剛剛指定的視覺樣式建立視窗。

cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
    swa.colormap = cmap;
    swa.border_pixel = 0;
    swa.event_mask = KeyPressMask | ExposureMask | ButtonPressMask | StructureNotifyMask;
    win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0,
                        glf_WinWidth, glf_WinHeight, 0, vi->depth, InputOutput, vi->visual,
                        CWBorderPixel | CWColormap | CWEventMask, &swa);
    XSetStandardProperties(dpy, win, "main", "main", None, argv, argc, NULL);
           

  再将剛剛建立的OpenGL狀态資訊,綁定到視窗。

glXMakeCurrent(dpy, win, cx);
           

  最後,調用XMapWindow函數将視窗顯示在桌面上就完成了。

XMapWindow(dpy, win);
           
Linux系統下的OpenGL圖形程式架構            Linux系統下的OpenGL圖形程式架構

2.OpenGL初始化

        編寫一個glfInit()函數用來進行對OpenGL的初始化,如:調整投影變換,設定視口,etc.這個函數定義在glFrame.h中。        

void glfInit()
{
    glViewport(0, 0, glf_WinWidth, glf_WinHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glf_left=0.0f;
    glf_right=50.0f;
    glf_bottom=0.0f;
    glf_top=50.0f;
    glf_near=-1.0f;
    glf_far=1.0f;
    glOrtho(glf_left,glf_right,glf_bottom,glf_top,glf_near,glf_far);
    glMatrixMode(GL_MODELVIEW);
}
           

3.接受和處理XEvent事件

        寫一個while循環,調用XNextEvent函數來接收滑鼠和鍵盤事件。XEvent的定義在Xlib.h中找到。在XEvent中有一個變量type,辨別了事件的種類。是以我們可以寫一個switch語句來,對不同的事件進行不同的處理。         對于鍵盤事件,編寫一個glfProcessKeyboard函數來對其進行處理,參數就是按鍵的所代表的值。如:XK_Escape,etc。這些宏都定義在keysymdef.h中。         對于滑鼠事件,編寫一個glfProcessMousePress函數來對滑鼠按鍵事件進行處理,參數是滑鼠按鍵時的位置x和y,左上方為原點。         在處理了這些事件後,修改布爾變量needUpdate的值,告訴glFrame是否需要進行更新操作。

if (needUpdate)
        {
            glfUpdate();                                                          //glFrame更新
            needUpdate = GL_FALSE;
            needDraw = GL_TRUE;
        }
        if (needDraw)
        {
            glfDraw();                                                            //glFrame繪制
            needDraw = GL_FALSE;
        }
           

4.glfFrame.h

        在前面的内容裡,一個基于OpenGL的Linux圖形程式架構以及基本搭建完成了。這個時候,編譯運作可以看到:

Linux系統下的OpenGL圖形程式架構            Linux系統下的OpenGL圖形程式架構

        可以注意到,所有和OpenGL相關的編碼,都被放到了glfFrame.h中,于是我們的工作隻需要修改glfFrame裡面的内容就能夠自由的進行繪制工作,并且響應滑鼠鍵盤事件。         為了能夠實作更多的功能,我為這個架構實作了按鈕和标簽。在font.h,定義了26個英文大寫字母的位圖矩形數組。使用glBitmap函數以及顯示清單機制使我們能夠很友善地使用光栅字型。         列印字母的具體做法:

glRasterPos2i(label.posx,label.posy);
    printString(label.str);
           

            當然在使用光栅字型的時候,你需要先調用makeRasterFont() 函數來生成這26個顯示清單。做法來源于紅書。        于是,有了這個顯示字型的工具,我們可以定義标簽Label:

struct Label
{
    GLboolean isShow;          //是否顯示
    char str[100];             //顯示的内容
    GLint posx;                //光栅位置x
    GLint posy;                //光栅位置y
    GLfloat color[4];          //顔色
};
           

           以及繪制标簽的函數GLboolean glfDrawLabel(Label label)

GLboolean glfDrawLabel(Label label)
{
    if (label.isShow == GL_FALSE)
        return GL_FALSE;
    glPushAttrib(GL_CURRENT_BIT);
    glColor3fv(label.color);
    glRasterPos2i(label.posx,label.posy);
    printString(label.str);
    glPopAttrib();
    return GL_TRUE;
}
           

           對于按鈕來說,當一個頁面裡面有很多按鈕時,glFrame需要知道是那一個按鈕被按下了,進而進行對應的操作。于是定義一個全局變量

typedef char ButtonEvent;

ButtonEvent glf_buttonevent[10];         當檢測到按鈕被摁下時,就把按鈕的event資訊儲存到glf_buttonevent裡面,按鈕的event資訊需要事先設定好。         于是對按鈕的結構體有定義:

struct Button
{
    GLboolean      isAround;
    GLfloat        aroundColor[4];
    GLint          aroundLineWidth;
    GLboolean      isShow;
    Label          text;
    Point2D        leftDown;
    Point2D        leftUp;
    Point2D        rightDown;
    Point2D        rightUp;
    GLfloat        color[4];
    ButtonEvent    event[10];
};
           

        以及按鈕的摁下檢測函數

GLboolean glfCheckButtonPress(int ix,int iy,Button button)
{
    double x,y; 
    iy=glf_WinHeight-iy;                                             
//将滑鼠的位置坐标轉化為OpenGL坐标系中
    x=(double)ix/(double)glf_WinWidth*(glf_right-glf_left);          //把坐标轉化到視景體中
    y=(double)iy/(double)glf_WinHeight*(glf_right-glf_left);         //把坐标轉化到視景體中
    if (x <= button.rightDown.p[0] && x >= button.leftDown.p[0]
     && y <= button.rightUp.p[1] && y >= button.rightDown.p[1])
    {
        strcpy(glf_buttonevent,button.event);
        return GL_TRUE;
    }
    return GL_FALSE;
}
           

        最後就是按鈕的繪制函數

GLboolean glfDrawButton(Button button)
{
    if (button.isShow == GL_FALSE)
        return GL_FALSE;
    glPushAttrib(GL_CURRENT_BIT);
    glColor3fv(button.color);
    glBegin(GL_QUADS);
        glVertex2fv(button.leftDown.p);
        glVertex2fv(button.leftUp.p);
        glVertex2fv(button.rightUp.p);
        glVertex2fv(button.rightDown.p);
    glEnd();
    glfDrawLabel(button.text);
    if (button.isAround == GL_TRUE)
    {
        glColor3fv(button.aroundColor);
        glLineWidth(button.aroundLineWidth);
        glBegin(GL_LINES);
            glVertex2fv(button.rightDown.p);
            glVertex2fv(button.leftDown.p);
        glEnd();
        glBegin(GL_LINES);
            glVertex2fv(button.leftDown.p);
            glVertex2fv(button.leftUp.p);
        glEnd();
        glBegin(GL_LINES);
            glVertex2fv(button.leftUp.p);
            glVertex2fv(button.rightUp.p);
        glEnd();
        glBegin(GL_LINES);
            glVertex2fv(button.rightUp.p);
            glVertex2fv(button.rightDown.p);
        glEnd();
    }
    glPopAttrib();
    return GL_TRUE;
}
           

        最終的效果圖:

Linux系統下的OpenGL圖形程式架構            Linux系統下的OpenGL圖形程式架構

        本文的全部代碼以及上傳到了Great-Code上,請猛擊此處。 附glFrame.h代碼:

#ifndef GLFRAME_H_INCLUDED
#define GLFRAME_H_INCLUDED
#include "global.h"
#include "font.h"
#include "Label.h"
#include "Button.h"
void glfPrint(GLboolean doubleBuffer);
void glfInit();
void glfProcessKeyboard(KeySym keysym);
void glfProcessMouseClick(int x,int y);
void glfprocessButtonPress();
void glfUpdate();
void glfReshape(int width,int height);
void glfDraw();
Label label;
Button button;
Button b_exit;
void glfPrint(GLboolean doubleBuffer)
{
    if (doubleBuffer)
        glXSwapBuffers(dpy, win);
    else
        glFlush();
}
void glfInit()
{
    glViewport(0, 0, glf_WinWidth, glf_WinHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glf_left=0.0f;
    glf_right=50.0f;
    glf_bottom=0.0f;
    glf_top=50.0f;
    glf_near=-1.0f;
    glf_far=1.0f;
    glOrtho(glf_left,glf_right,glf_bottom,glf_top,glf_near,glf_far);
    glMatrixMode(GL_MODELVIEW);
    makeRasterFont();

    label.isShow=GL_TRUE;
    label.color[0]=1.0f;
    label.color[1]=1.0f;
    label.color[2]=0.0f;
    label.color[3]=0.0f;
    label.posx=15;
    label.posy=40;
    strcpy(label.str,"HELLO SSSOGS");

    button.isShow=GL_TRUE;
    button.isAround=GL_TRUE;
    button.aroundLineWidth=3;
    button.aroundColor[0]=0.0f;
    button.aroundColor[1]=1.0f;
    button.aroundColor[2]=0.0f;
    button.aroundColor[3]=0.0f;
    button.leftDown.p[0]=4;
    button.leftDown.p[1]=20;
    button.leftUp.p[0]=4;
    button.leftUp.p[1]=30;
    button.rightDown.p[0]=24;
    button.rightDown.p[1]=20;
    button.rightUp.p[0]=24;
    button.rightUp.p[1]=30;
    button.color[0]=0.9f;
    button.color[1]=0.5f;
    button.color[2]=0.9f;
    button.color[3]=0.0f;
    button.text.isShow=GL_TRUE;
    button.text.color[0]=0.0f;
    button.text.color[1]=0.0f;
    button.text.color[2]=0.0f;
    button.text.color[3]=0.0f;
    button.text.posx=6;
    button.text.posy=25;
    strcpy(button.text.str,"HIDE WORDS");
    strcpy(button.event,"Test");

    b_exit.isShow=GL_TRUE;
    b_exit.isAround=GL_TRUE;
    b_exit.aroundLineWidth=3;
    b_exit.aroundColor[0]=0.0f;
    b_exit.aroundColor[1]=1.0f;
    b_exit.aroundColor[2]=0.0f;
    b_exit.aroundColor[3]=0.0f;
    b_exit.leftDown.p[0]=26;
    b_exit.leftDown.p[1]=20;
    b_exit.leftUp.p[0]=26;
    b_exit.leftUp.p[1]=30;
    b_exit.rightDown.p[0]=46;
    b_exit.rightDown.p[1]=20;
    b_exit.rightUp.p[0]=46;
    b_exit.rightUp.p[1]=30;
    b_exit.color[0]=0.9f;
    b_exit.color[1]=0.5f;
    b_exit.color[2]=0.9f;
    b_exit.color[3]=0.0f;
    b_exit.text.isShow=GL_TRUE;
    b_exit.text.color[0]=0.0f;
    b_exit.text.color[1]=0.0f;
    b_exit.text.color[2]=0.0f;
    b_exit.text.color[3]=0.0f;
    b_exit.text.posx=28;
    b_exit.text.posy=25;
    strcpy(b_exit.text.str,"EXIT");
    strcpy(b_exit.event,"exit");
}

void glfProcessKeyboard(KeySym keysym)
{
    if (keysym == (KeySym)XK_Escape)
        exit(0);
}
void glfProcessMousePress(int x,int y)
{
    if (glfCheckButtonPress(x,y,button) == GL_TRUE)
    {
        glfprocessButtonPress();
    }
    if (glfCheckButtonPress(x,y,b_exit) == GL_TRUE)
    {
        glfprocessButtonPress();
    }
}
void glfprocessButtonPress()
{
    if (glf_buttonevent == NULL)
        return ;
    if (strcmp(glf_buttonevent,"Test") == 0)
    {
        label.isShow=!label.isShow;
        if (label.isShow)
        {
            strcpy(button.text.str,"HIDE WORDS");
        }
        else
        {
            strcpy(button.text.str,"SHOW WORDS");
        }

    }

    if (strcmp(glf_buttonevent,"exit") == 0)
    {
        exit(0);
    }
}
void glfUpdate()
{

}
void glfReshape(int width,int height)
{
    glf_WinWidth=width;
    glf_WinHeight=height;
    glViewport(0,0,glf_WinWidth,glf_WinHeight);
}
void glfDraw()
{
    glClearColor(0.0,0.0,0.0,0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glfDrawLabel(label);
    glfDrawButton(button);
    glfDrawButton(b_exit);
    glfPrint(glf_DoubleBuffer);
}

#endif // GLFRAME_H_INCLUDED
           

繼續閱讀