Qt下的OpenGL 编程(3)绘制平面几何体

一、 提要
   之前的一篇教程已经搭建好了Qt下的OpenGL的编程环境,几天要来学习的就是OpenGL的2D绘图。
2D作为绘图的基础,还是很值得去好好学习,比如迪卡尔坐标,透视设置等等,而所谓的3D,也只是在2D的基础上加上了Z轴。
   这篇教程主要包括基本2D图元的绘制,着色,旋转。

二、必须要了解的几个函数
gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar);
   这个函数指定了观察的视景体在世界坐标系中的具体大小,一般而言,其中的参数aspect应该与窗口的宽高比大小相同。
fovy,视野角度,用过照相机的话就很好理解了,数值越小,相当于将镜头拉得越近,数值越大,镜头越广,镜头里的东西就越小。
aspect,这个好理解,就是实际窗口的纵横比,即x/y。
zNear,这个呢,表示你近处,的裁面。
zFar表示远处的裁面。

 
glLoadIdentity();
这个函数类似于一个复位操作:
1.X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
2.OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。
3.中心左面的坐标值是负值,右面是正值。
移向屏幕顶端是正值,移向屏幕底端是负值。
移入屏幕深处是负值,移出屏幕则是正值。

 
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
这个函数设置清除屏幕时所用的颜色,色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
 
glTranslatef(x, y, z)沿着 X, Y  Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。

glVertex *
glVertex3f 就是确定顶点的函数,三个参数是点 的空间坐标。


 
 
三.图形的绘制
在上一篇教程中已经绘制了一个正方形,相信大家都不会感到陌生。
先来分析上一篇的绘图代码:

 
 
  1. //绘制一个正方形  
  2. glBegin( GL_QUADS );  
  3. glVertex3f( -1.0,  1.0,  0.0 );  
  4. glVertex3f(  1.0,  1.0,  0.0 );  
  5. glVertex3f(  1.0, -1.0,  0.0 );  
  6. glVertex3f( -1.0, -1.0,  0.0 );  
  7. glEnd();  
 
glBegin  glEnd 很明显是一对, 标志着一组 OpenGL 操作的开始和结束。 并且在参数中告诉了 OpenGL 下面的操作是针对什么图形进行的,此例中 GL_QUADS 是表示四边形。
通过传递给glBegin不同的参数,我们可以完成各种不同的图形绘制,点,线,三角形,矩形,不规则多边形...

 
简而言之,上面的 4  glVertex3f 确定了矩形的 4 个顶点。(注意顺序)然后, OpenGL 就会自动根据 glBegin 指定的参数去完成相关的绘制任务了。
 
 
下面我们在屏幕上绘制一个三角形和一个圆形,还有一个多边形。
修改nehewidget.cpp的paintGL()就可以了。

 
 
  1. void NeHeWidget::paintGL()  
  2. {  
  3.     // 清除屏幕和深度缓存  
  4.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  
  5.     glLoadIdentity();  
  6.     //移到屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景  
  7.     glTranslatef(-2.0f,0.0f,-5.0f);  
  8.     //设置颜色  
  9.     glColor3f( 1.0, 1.0, 1.0 );  
  10.     //绘制一个三角形  
  11.     glBegin(GL_TRIANGLES);                          // 绘制三角形  
  12.             glVertex3f( 0.0f, 1.0f, 0.0f);                  // 上顶点  
  13.             glVertex3f(-1.0f,-1.0f, 0.0f);                  // 左下  
  14.             glVertex3f( 1.0f,-1.0f, 0.0f);                  // 右下  
  15.     glEnd();  
  16.     glLoadIdentity();  
  17.     glTranslatef(0.0f,0.0f,-5.0f);  
  18.     //绘制圆形  
  19.     GLint iCirclePoints = 50;  
  20.           glBegin (GL_TRIANGLE_FAN );  
  21.           for (int i = 0; i < 50; ++i )  
  22.           {  
  23.                  double dAngle = 2 * PI * i / iCirclePoints ;  
  24.                  glVertex3f (cos (dAngle ), sin (dAngle ), 0.0);  
  25.           }  
  26.           glEnd ();  
  27.       //绘制多边形  
  28.           glLoadIdentity();  
  29.           glTranslatef(2.0f,0.0f,-5.0f);  
  30.           glBegin(GL_POLYGON);  
  31.                   glVertex3f( 0.0f, 0.5f, 0.0f);  
  32.                   glVertex3f(-0.5f,-0.5f, 0.0f);  
  33.                   glVertex3f( 0.0f,-1.5f, 0.0f);  
  34.                   glVertex3f( 0.5f,-0.5f, 0.0f);  
  35.          glEnd();  
  36. }  

 
三角和多边形非常简单,就是将对应的参数传给glBegin(),然后给出坐标glEnd()之后就会绘制。圆形的方法就是将整个圆周分成50份,然后利用三角函数循环计算圆周上的点的值,GL_TRIANGLE_FAN 是指连续地绘制三角形。三角形和多边形的绘制就比较简单了,就是在glBegin()和glEnd()之间插入相应的顶点坐标就可以了,可以去参考红宝书里第二章的内容。

 
四. 着色
        关于着色,其实我们上面已经用到一个函数,就是glColor3f( 1.0, 1.0, 1.0 );原理也是通过设置三个参数,得到想要的颜色,然后用来绘制图形。注意,这里的着色是对于顶点的,也就是说你可以在绘制每个顶点之前设置绘制的颜色,然后它们之间的区域会很平滑的过渡。
        继续修改代码。

        void NeHeWidget::paintGL()
        {
            // 清除屏幕和深度缓存
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
            glLoadIdentity();
            //移到屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景
            glTranslatef(-2.0f,0.0f,-5.0f);
 
            //绘制一个三角形
            glBegin(GL_TRIANGLES);							// 绘制三角形
                    glColor3f(1.0f,0.0f,0.0f);
                    glVertex3f( 0.0f, 1.0f, 0.0f);					// 上顶点
                    glColor3f(0.0f,1.0f,0.0f);
                    glVertex3f(-1.0f,-1.0f, 0.0f);					// 左下
                    glColor3f(0.0f,0.0f,1.0f);
                    glVertex3f( 1.0f,-1.0f, 0.0f);					// 右下
            glEnd();
 
            glLoadIdentity();
            glTranslatef(0.0f,0.0f,-5.0f);
            //绘制圆形
            GLint iCirclePoints = 50;
                  glBegin (GL_TRIANGLE_FAN );
                  for (int i = 0; i < 50; ++i )
                  {
                        glColor3f (0.0/i, 1.0/i, 1.0/i);
                         double dAngle = 2 * PI * i / iCirclePoints ;
                         glVertex3f (cos (dAngle ), sin (dAngle ), 0.0);
                  }
                  glEnd ();
 
              //绘制多边形
                  glLoadIdentity();
                  glTranslatef(2.0f,0.0f,-5.0f);
                  glColor3f( 1.0, 0.5, 0.0 );
                  glBegin(GL_POLYGON);
                          glVertex3f( 0.0f, 0.5f, 0.0f);					// 上顶点
                          glVertex3f(-0.5f,-0.5f, 0.0f);
                          glVertex3f( 0.0f,-1.5f, 0.0f);// 左下
                          glVertex3f( 0.5f,-0.5f, 0.0f);					// 右下
                 glEnd();
    }
 

 
    对于三角形,glEnd()出现后,三角形将被填充。但是因为每个顶点有不同的颜色,因此看起来颜色从每个角喷出,并刚好在三角形的中心汇合,三种颜色相互混合。这就是平滑着色。
    对于圆形,就是利用循环逐渐改变顶点的颜色,最后得到这种渐变的效果。
    对于多边形,因为在glBegin()之前将颜色已经设置好,而在后面也没有改变,所以最后所有顶点都设置成了同一种颜色。

 
五.旋转
    旋转其实已经属于3维的内容了,但实现起来其实非常简单。我们将在NeHeWidget类中增加三个变量来控制这三个对象的旋转。它们是浮点类型的变量,使得我们能够非常精确地旋转对象。新变量中叫做rTri的用来旋转三角形,rCir旋转圆形,rPoly 旋转多边边形。
    旋转所用到的函数是glRotatef( Angle, Xvector, Yvector, Zvector ),
    它负责让对象绕某个轴旋转。这个函数有很多用处。 Angle 通常是个变量代表对象转过的角度。Xvector,Yvector和Zvector三个参数则共同决定旋转轴的方向。比如( 1, 0, 0 )所描述的矢量经过X坐标轴的1个单位处并且方向向右。( -1, 0, 0 )所描述的矢量经过X坐标轴的1个单位处,但方向向左。
    关于旋转的方向的确定,我们可以这样来理解:( Xvector, Yvector, Zvector)表示空间中的一个向量,用右手的大拇指指向这个向量,其他四指抓握的方向就是旋转的方向。
    继续修改代码.

    头文件中添加相应的变量:
    protected:
    GLfloat rTri;
    GLfloat rCir;
    GLfloat rPoly;
    在构造函数中初始化
    NeHeWidget::NeHeWidget(QWidget *parent) :
        QGLWidget(parent)
    {
    //初始化旋转量
        rTri = 0;
         rCir = 0;
         rPoly=0;
    void NeHeWidget::paintGL()
    {
        // 清除屏幕和深度缓存
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        glLoadIdentity();
        //移到屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景
        glTranslatef(-2.0f,0.0f,-5.0f);
        //对三角形进行旋转
        glRotatef( rTri,  1.0,  0.0,  0.0 );
        //绘制一个三角形
        glBegin(GL_TRIANGLES);							// 绘制三角形
                glColor3f(1.0f,0.0f,0.0f);
                glVertex3f( 0.0f, 1.0f, 0.0f);					// 上顶点
                glColor3f(0.0f,1.0f,0.0f);
                glVertex3f(-1.0f,-1.0f, 0.0f);					// 左下
                glColor3f(0.0f,0.0f,1.0f);
                glVertex3f( 1.0f,-1.0f, 0.0f);					// 右下
        glEnd();
 
        glLoadIdentity();
        glTranslatef(0.0f,0.0f,-5.0f);
 
        glRotatef( rCir,  0.0,  1.0,  0.0 );
        //绘制圆形
        GLint iCirclePoints = 50;
              glBegin (GL_TRIANGLE_FAN );
              for (int i = 0; i < 50; ++i )
              {
                    glColor3f (0.0/i, 1.0/i, 1.0/i);
                     double dAngle = 2 * PI * i / iCirclePoints ;
                     glVertex3f (cos (dAngle ), sin (dAngle ), 0.0);
              }
              glEnd ();
 
          //绘制多边形
              glLoadIdentity();
              glTranslatef(2.0f,0.0f,-5.0f);
              glRotatef( rPoly,  0.0,  1.0,  1.0 );
              glColor3f( 1.0, 0.5, 0.0 );
              glBegin(GL_POLYGON);
                      glVertex3f( 0.0f, 0.5f, 0.0f);					// 上顶点
                      glVertex3f(-0.5f,-0.5f, 0.0f);
                      glVertex3f( 0.0f,-1.5f, 0.0f);// 左下
                      glVertex3f( 0.5f,-0.5f, 0.0f);					// 右下
             glEnd();
             //每重新绘制一遍窗口就改变一次旋转量。
             rTri += 1.5;
             rCir -= 1.5;
             rPoly+=2;
    }
 

 
    编译运行之后不断按F2改变窗口就可以观察到相应的变化了,这里我通过设定不同的旋转向量来让三个图形按照不同的方向来旋转。
     注意一点:在适当的位置调用glLoadIdentity()重置坐标,不然在后面图形的绘制的时候就是基于之前变换过的坐标了。
    今天就到这。

参考资料
 
 

1.      《 OpenGL Reference Manual 》, OpenGL 参考手册

2.      《 OpenGL 编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis 著,徐波译,机械工业出版社

3.         《win32 OpenGL编程 》   一个大牛的博客      http://blog.csdn.net/vagrxie/article/category/628716/3
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值