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);


  

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图形程序框架以及基本搭建完成了。这个时候,编译运行可以看到:

        可以注意到,所有和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;
}

        最终的效果图:

        本文的全部代码以及上传到了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



你可能感兴趣的:(OpenGL学习,Linux学习)