OpenGL实验一:画图小程序

要求:开发一个画图程序,用户可以用鼠标绘制线段、矩形、圆和三角形等。通过菜单让用户选择需要绘制的图元。

框架参考

/* globals */
GLsizei wh = 500, ww = 500; /* initial window size */
float xm, ym, xmm, ymm;
int first = 0;
void mouse(int btn, int state, int x, int y)
{
    if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
    {
        xm = x;
        ym = wh-y;
        glColor3f(0.0, 0.0, 1.0);
        glLogicOp(GL_XOR);
        first = 0;
    }

    if(btn==GLUT_LEFT_BUTTON && state==GLUT_UP)
    {
        if (first == 1)
        {
            glRectf(xm, ym, xmm, ymm);
            glFlush();
        }

        xmm = x;
        ymm = wh-y;
        glColor3f(0.0, 1.0, 0.0);
        glLogicOp(GL_COPY);
        glRectf(xm, ym, xmm, ymm);
        glFlush();
    }

    if(btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN)  
        exit(0);
}

void move(int x, int y)
{
    if(first == 1)
    {
        glRectf(xm, ym, xmm, ymm);
        glFlush();
    }

    xmm = x;
    ymm = wh-y;
    glRectf(xm, ym, xmm, ymm);
    glFlush();
    first = 1;
}

void reshape(GLsizei w, GLsizei h)
{
    
    /* adjust clipping box */
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); 
    glOrtho(0.0,(GLdouble)w, 0.0,(GLdouble)h, -1.0, 1.0);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity(); 
    
    /* adjust viewport and clear */
    
    glViewport(0,0,w,h);
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();
    
    /* set global size for use by drawing routine */
    
    ww = w;
    wh = h; 
}

void init(void)
{
    glViewport(0,0,ww,wh);
    
    /* Pick 2D clipping window to match size of screen window 
    This choice avoids having to scale object coordinates
    each time window is resized */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); 
    glOrtho(0.0,(GLdouble) ww , 0.0,(GLdouble) wh , -1.0, 1.0);
    
    /* set clear color to black and clear window */
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();  

    glEnable(GL_COLOR_LOGIC_OP);
}


/* display callback required by GLUT 3.0 */
void display(void)
{}

int main(int argc, char** argv)
{
    /***GLUT窗口管理,就当做是固定写法吧***/
    glutInit(&argc,argv);                         //初始化GLUT,在调用其他GLUT函数前调用
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);  //设置窗口的显示模式
    glutInitWindowSize(ww, wh);                   //设置窗口的初始宽度和高度,单位为像素,缺省300x300
    glutInitWindowPosition(500, 500)              //窗口左上角相对于屏幕左上角的位置,单位为像素,缺省(0,0) 
    glutCreateWindow("rubber banding");           //创建窗口,标题为title。调用glutMainLoop()之前,窗口不会被显示
    
    init();  //设置OpenGL状态

    /***GLUT事件处理***/
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMotionFunc(move);
    glutDisplayFunc(display);
    glutMainLoop();
}

OpenGL库

首先你会发现这些函数有些不是以gl开头就是以glut开头,以什么开头就代表是哪个库中的函数。

  • OpenGL核心库(OpenGL Core Library)
    gl是核心,这部分函数用于常规的、核心的图形处理。
  • OpenGL实用库(OpenGL Utility Library, GLU)
    glu是对gl的部分封装,glut是OpenGL的跨平台工具库,gl中包含了最基本的3D函数,而glu似乎对gl的辅助,如果算数好,不用glu的情况下,也是可以做出同样的效果。
  • OpenGL实用工具库(OpenGL Utility Toolkit Library, GLUT)
    glut是基本的窗口界面,是独立于gl和glu的,如果不喜欢用glut可以用MFC和Win32窗口等代替,但是glut是跨平台的,这就保证了我们编出的程序是跨平台的。

OpenGL函数名称格式

OpenGL实验一:画图小程序_第1张图片
OpenGL函数名称格式

GLUT事件处理

我们这里界面都是采用GLUT库来写。GLUT使用回调函数(callback)机制来进行事件处理。

  • 啥叫回调函数呢(callback)?
    回调就是你自己定义一个函数(比如mouse),然后把这个函数作为参数传入别的(或系统)函数(比如glutMouseFunc)中,由别人(或系统)的函数在运行时来调用的函数。简单来说,就是由别人的函数运行期间回过来调用你已经实现的那个函数。
  • void glutDisplayFunc(void(*func)(void))
    每个GLUT程序都必须有一个显式回调函数,啥作用呢?比如窗口首次被打开,或者窗口移动了,在窗口中切换绘图选项时,系统都会自动调用这个函数,继而调用传入其中的函数。
  • 函数指针
    上面那个函数的参数void(*func)(void)其实就是个函数指针。这个原理和int a是一样的,int是参数a的类型, a是参数名;类比一下,这里函数指针类型是void(* )(void),这里参数是个函数指针类型的变量,参数名是func;其实每个函数的函数名都可以说是一个函数指针名,我们在用的时候才只要闯入相应的函数名就实现了所谓的回调。所以专业点说,回调函数就是一个通过函数指针调用的函数。
  • void glutMainLoop()
    进入GLUT事件处理循,环实际就是个死循环啦。当有事件发生时,调用相应的回调函数;否则,处于等待状态。main()函数是以程序进入事件循环做为结束,而不是我们通常所写的return 0;
  • init()函数
    这个函数中我们定义了一些OpenGL的状态量,个人认为和glutDisplayFunc里面将要传入的函数(比如这里的display)有些雷同,所以上面的display()函数我们虽然写了,但却是个空函数,(必须有这个display函数),因为一些状态我们都在init函数中设置了。里面涉及到了视图等的设置,这些之后会讲到,现在只需要这样跟着写就好了。
  • GLUT使用回调函数机制来进行事件处理,我们的程序中主要用到了前4个:
    –窗口重绘glutDisplayFunc()
    –窗口改变大小glutReshapeFunc()
    –鼠标按键glutMouseFunc()
    –鼠标移动glutMotionFunc()
    –键盘输入glutKeyboardFunc()
    –空闲函数glutIdleFunc()

窗口

窗口是显示器上的一块矩形区域。窗口内的位置用窗口坐标来指定,单位是像素。

注意原点的位置

  • 光栅显示器由于是按照从上到下,从左到右的顺序进行扫描,所以左上角是原点。窗口系统返回的信息(例如鼠标位置)假定原点在左上角
  • 学科学和工程中,左下角是原点(0,0)。OpenGL命令假定原点在左下角

所以在画图的时候,我们要把鼠标位置的坐标转换成OpenGL命令中对应的坐标。原点在左上角和左下角相比较一下,不就是x坐标不用变,y坐标是个互补的关系嘛,用窗口的高度(wh)减掉鼠标位置的y坐标就得到OpenGL命令中的坐标啦。

画直线要求的橡皮条功能

这里用到一个函数glLogicOp

  • glLogicOp(GL_COPY) (默认值)
    缺省的写入模式是复制模式(copy)或替换模式 (replacement),直接用源像素取代目标像素。用这种方法不能绘制一条临时直线,因为我们不能用快速简 单的方法恢复在临时直线下面的内容。
  • glLogicOp(GL_XOR)
    XOR写入模式。 异或操作(XOR):相同值为0,不同值为1
    因此如果应用XOR模式画一条直线,那么只要在原地再画 一遍这条直线就可以删除这条直线。

画圆

OpenGL没有提供直接画圆的函数,实现方式用一个多边形来逼近。大家都知道正多边形的边如越多越接近圆吧~
这里我用到了G_TRIANGLE_FAN这个参数,书上都有讲解。
截取一部分代码:

if (btn == GLUT_LEFT_BUTTON && state == GLUT_UP)
    {
        xmm = x;
        ymm = wh - y;
        float r = sqrt(pow((xm - xmm), 2) + pow((ym - ymm), 2));
        glBegin(GL_TRIANGLE_FAN);
        for (int i = 0; i< 1000; ++i){
            glVertex2f(xm + r*cos(2 * PI / 1000 * i), ym + r*sin(2 * PI / 1000 * i));
        }
        glEnd();
        glFlush();
    }

画三角形

简单的一个实现的思路:用一个变量将三个点的坐标保存起来,每点击一次鼠标记录一个位置并计数,当点数满三个的时候,就把这个三角形画出来。
截取一部分代码:

void mouse_triangle(int btn, int state, int x, int y)
{
    if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
        triVertex[ver][0] = x;
        triVertex[ver][1] = wh - y;
        glColor3f(1.0, 0.5, 0.0);
        glBegin(GL_POINTS);
        glVertex2f(triVertex[ver][0], triVertex[ver][1]);
        glEnd();
        glFlush();
        ver++;
        if (ver == 3)
        {   
            //glLogicOp(GL_COPY);//直接用源像素取代目标像素 默认的
            glBegin(GL_TRIANGLES);
            glVertex2f(triVertex[0][0], triVertex[0][1]);
            glVertex2f(triVertex[1][0], triVertex[1][1]);
            glVertex2f(triVertex[2][0], triVertex[2][1]);
            glEnd();
            glFlush();
            ver = 0;
        }

    }
}

添加菜单

用到如下的三个函数,比如:

    glutCreateMenu(menu); //创建菜单
    glutAddMenuEntry("line", 0);  //在菜单中添加选项
    glutAddMenuEntry("square", 1);
    glutAddMenuEntry("triangle", 2);
    glutAddMenuEntry("circle", 3);
    glutAddMenuEntry("clear", 4);
    glutAttachMenu(GLUT_RIGHT_BUTTON); //将菜单绑定鼠标操作

你可能感兴趣的:(OpenGL实验一:画图小程序)