美文网首页
OpenGL实验一:画图小程序

OpenGL实验一:画图小程序

作者: Beatrice7 | 来源:发表于2016-03-16 19:07 被阅读3499次

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

    框架参考

    /* 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函数名称格式

    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实验一:画图小程序

          本文链接:https://www.haomeiwen.com/subject/hybqlttx.html