Qt下的OpenGL 编程纹理和贴图

 

Qt下的OpenGL 编程纹理和贴图





2、openGL坐标系编程

     OpenGL使用右手坐标,从左到右,x递增,从下到上,y递增,从远到近,z递增。
     OpenGL坐标系可分为:世界坐标系和当前绘图坐标系。

     世界坐标系以屏幕中心为原点(0, 0, 0)。你面对屏幕,你的右边是x正轴,上面是y正轴,屏幕指向你的为z正轴。长度单位这样来定: 窗口范围按此单位刚好是(-1,-1)到(1,1)。

      当前绘图坐标系是 绘制物体时的坐标系。程序刚初始化时,世界坐标系和当前绘图坐标系是重合的。当用glTranslatef(),glScalef(), glRotatef()对当前绘图坐标系进行平移、伸缩、旋转变换以后, 世界坐标系和当前绘图坐标系再也不重合。改变之后,再用glVertex3f()等绘图函数绘图时,都是在当前绘图坐标系进行绘图,全部的函数参数也都是相 对当前绘图坐标系来说的。


3、绘制一个三角锥和正方体
    绘制三角锥的方法就是在空间中连续地绘制四个三角形,最后造成一个封闭的体。
    修改void NeHeWidget::paintGL()。
 
 

[cpp] view plain copy 在CODE上查看代码片派生到个人代码片浏览器

  1. void NeHeWidget::paintGL()  缓存

  2. {  ide

  3.     // 清除屏幕和深度缓存  函数

  4.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  ui

  5.     glLoadIdentity();  url

  6.     //移到屏幕的左半部分,而且将视图推入屏幕背后足够的距离以便咱们能够看见所有的场景  spa

  7.     glTranslatef(-1.0f,0.0f,-6.0f);  .net

  8.     glRotatef(rTri,1.0,0,0);  设计

  9.     glBegin( GL_TRIANGLE_STRIP );  

  10.     glColor3f( 1.0, 0.0, 0.0 );  

  11.     glVertex3f(  0.0,  1.0,  0.0 );  

  12.     glColor3f( 0.0, 1.0, 0.0 );  

  13.     glVertex3f(-1.0, -1.0,  1.0 );  

  14.     glColor3f( 0.0, 0.0, 1.0 );  

  15.     glVertex3f(  1.0, -1.0,  1.0 );  

  16.     glColor3f( 0.0, 1.0, 0.0 );  

  17.     glVertex3f(  1.0, -1.0, -1.0 );  

  18.     glColor3f( 1.0, 0.0, 0.0 );  

  19.     glVertex3f(  0.0,  1.0,  0.0 );  

  20.     glColor3f( 0.0, 1.0, 0.0 );  

  21.     glVertex3f(-1.0, -1.0,  1.0 );  

  22.     glEnd();  

  23.     rTri+=2;  

  24. }  



   在写代码以前,最好在草稿纸上画出三角锥的空间模型,算出每一个点的坐标。这里,传给glBegin的是GL_TRIANGLE_STRIP ,
    意思就是连续地绘制多个三角行,前两个点就和第三个点组成三角形。
    因此一共画四个面,定义了5个点,最后;两个点和最开始的两个点是重合的。
    由于在定义了每一个顶点的颜色,因此最后每一个面都有很漂亮的过分色。

    咱们用一样的思路来绘制一个正方体。这里传给glBegin的参数是GL_QUAD_STRIP,意思就是连续地绘制正方形。

 
 

[cpp] view plain copy 在CODE上查看代码片派生到个人代码片

  1. glLoadIdentity();  

  2. //移到屏幕的右半部分,而且将视图推入屏幕背后足够的距离以便咱们能够看见所有的场景  

  3. glTranslatef(1.0f,0.0f,-6.0f);  

  4. glRotatef(rTri,1.0,0,0);  

  5. glBegin(GL_QUAD_STRIP);  

  6. //第一面  

  7. glColor3f( 1.0, 0.0, 0.0 );  

  8. glVertex3f(  0.0,  0.0,  0.0 );  

  9. glVertex3f(  0.0,  0.0,  1.0 );  

  10. glVertex3f(  1.0,  0.0,  0.0 );  

  11. glVertex3f(  1.0,  0.0,  1.0 );  

  12. //第二个 面  

  13. glColor3f( 0.0, 1.0, 0.0 );  

  14. glVertex3f(  1.0,  1.0,  0.0 );  

  15. glVertex3f(  1.0,  1.0,  1.0 );  

  16. //第三个面  

  17. glColor3f( 0.0, 0.0, 1.0 );  

  18. glVertex3f(  0.0,  1.0,  0.0 );  

  19. glVertex3f(  0.0,  1.0,  1.0 );  

  20. //第四个面  

  21. glColor3f( 1.0, 0.0, 0.0 );  

  22. glVertex3f(  0.0,  0.0,  0.0 );  

  23. glVertex3f(  0.0,  0.0,  1.0 );  

  24. glEnd();  

  25. //第五个和第六个面  

  26. glBegin(GL_QUADS);  

  27. glColor3f( 0.0, 0.8, 0.8 );  

  28. glVertex3f(0.0,1.0,1.0);  

  29. glVertex3f(0.0,0.0,1.0);  

  30. glVertex3f(1.0,0.0,1.0);  

  31. glVertex3f(1.0,1.0,1.0);  

  32. glVertex3f(0.0,1.0,0.0);  

  33. glVertex3f(0.0,0.0,0.0);  

  34. glVertex3f(1.0,0.0,0.0);  

  35. glVertex3f(1.0,1.0,0.0);  

  36. glEnd();  





代码比较简单,最后的效果以下图:



注意全部的面都是逆时针次序绘制的。这点十分重要,这个和平面的正反面有关,之后应该会涉及到。因此要么都逆时针,要么都顺时针,但永远不要将两种次序混在一块儿,除非您有足够的理由必须这么作。


4、纹理映射
OpenGL纹理的使用分三步:将纹理装入内存,将纹理发送给OpenGL管道,给顶点指定纹理坐标.
修改nehewidget.h:
首先加入装载纹理的函数和几个变量:

//加载纹理函数
void loadGLTextures();
//正方体在三个方向上的旋转
GLfloat xRot, yRot, zRot;
//texture用来存储纹理
GLuint texture[1];

在构造函数中加入对旋转量的初始化:
xRot = yRot = zRot = 0.0;


接下来实现纹理装载函数:

 
 

[cpp] view plain copy 在CODE上查看代码片派生到个人代码片

  1. void NeHeWidget::loadGLTextures()  

  2. {  

  3.         QImage tex, buf;  

  4.       if ( !buf.load( ":/data/texture.jpg" ) )  

  5.       {  

  6.         //若是载入不成功,自动生成一个128*128的32位色的绿×××片。  

  7.           qWarning("Could not read p_w_picpath file!");  

  8.           QImage dummy( 128, 128,QImage::Format_RGB32 );  

  9.            dummy.fill( Qt::green );  

  10.              buf = dummy;  

  11.       }  

  12.       //转换成纹理类型  

  13.         tex = QGLWidget::convertToGLFormat( buf );  

  14.         //建立纹理  

  15.          glGenTextures( 1, &texture[0] );  

  16.         //使用来自位图数据生成的典型纹理,将纹理名字texture[0]绑定到纹理目标上  

  17.          glBindTexture( GL_TEXTURE_2D, texture[0] );  

  18.          glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,  

  19.                GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );  

  20.          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );  

  21.          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );  

  22. }  




解释几个函数:
函数原型:

      void glGenTextures(GLsizei n, GLuint *textures)

参数说明:

      n:用来生成纹理的数量
  textures:存储纹理索引的
函数说明:
  glGenTextures函数根据纹理参数返回n个纹理索引。纹理名称集合没必要是一个连续的整数集合。 (glGenTextures就是用来产生你要操做的纹理对象的索引的,好比你告诉OpenGL,我须要5个纹理对象,它会从没有用到的整数里返回5个给你)。

函数原型:

      void glBindTexture(GLenum target,   GLuint texture);

参数说明:

target:   纹理被绑定的目标,它只能取值GL_TEXTURE_1D或者GL_TEXTURE_2D;
texture :纹理的名称,而且,该纹理的名称在当前的应用中不能被再次使用。

函数说明:
glBindTexture其实是改变了OpenGL的这个状态,它告诉OpenGL下面对纹理的任何操做都是对它所绑定的纹理对象的,好比glBindTexture(GL_TEXTURE_2D,1)告诉OpenGL下面代码中对2D纹理的任何设置都是针对索引为1的纹理的。

函数原型:

      void glTexImage2D(GLenum target,GLint level,GLint components,GLsizei width, glsizei height,GLint border,GLenum format,GLenum type, const GLvoid *pixels);

函数说明:
    建立一个纹理。以咱们使用的那个函数为例,GL_TEXTURE_2D告诉OpenGL此纹理是一个2D纹理。数字零表明图像的详细程度,一般就由它为零去了。数字三是数据的成分数。由于图像是由红色数据,绿色数据,蓝色数据三种组分组成。 tex.width()是纹理的宽度。tex.height()是纹理的高度。数字零是边框的值,通常就是零。GL_RGBA 告诉OpenGL图像数据由红、绿、蓝三色数据以及alpha通道数据组成,这个是因为QGLWidget类的converToGLFormat()函数的缘由。 GL_UNSIGNED_BYTE 意味着组成图像的数据是无符号字节类型的。最后tex.bits()告诉OpenGL纹理数据的来源。


        最后的glTexParameteri()告诉OpenGL在显示图像时,当它比放大得原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小得比原始得纹理小(GL_TEXTURE_MIN_FILTER)时OpenGL采用的滤波方式。一般这两种状况下我都采用GL_LINEAR。这使得纹理从很远处到离屏幕很近时都平滑显示。使用GL_LINEAR须要CPU和显卡作更多的运算。若是您的机器很慢,您也许应该采用GL_NEAREST。过滤的纹理在放大的时候,看起来斑驳的很。您也能够结合这两种滤波方式。在近处时使用GL_LINEAR,远处时GL_NEAREST。

       插一句QPixmap和QImag的区别:
       QPixmap依赖于硬件,QImage不依赖于硬件。QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的。当图片小的状况下,直接用QPixmap进行加载,画图时无所谓,当图片大的时候若是直接用QPixmap进行加载,会占很大的内存,通常一张几十K的图片,用QPixmap加载进来会放大不少倍,因此通常图片大的状况下,用QImage进行加载,而后转乘QPixmap用户绘制。QPixmap绘制效果是最好的。

修改paintGL():
      在这里向你们推荐一个颇有意思的东东,就是Sumo Paint,它是Chrome浏览器里的一个绘图应用,用来在Ubuntu中进行图片编辑仍是很是不错的,咱们能够用它来编辑纹理贴图。
 
 

[cpp] view plain copy 在CODE上查看代码片派生到个人代码片

  1. void NeHeWidget::paintGL()  

  2. {  

  3.     // 清除屏幕和深度缓存  

  4.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  

  5.     glLoadIdentity();  

  6.     //移到屏幕的左半部分,而且将视图推入屏幕背后足够的距离以便咱们能够看见所有的场景  

  7.     glTranslatef(0.0f,0.0f,-5.0f);  

  8.     glRotatef( xRot,  1.0,  0.0,  0.0 );  

  9.     glRotatef( yRot,  0.0,  1.0,  0.0 );  

  10.     glRotatef( zRot,  0.0,  0.0,  1.0 );  

  11.     //选择使用的纹理  

  12.     glBindTexture( GL_TEXTURE_2D, texture[0] );  

  13.     glBegin( GL_QUADS );  

  14.     glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );  

  15.     glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );  

  16.     glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0,  1.0 );  

  17.     glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0,  1.0 );  

  18.     glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 );  

  19.     glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0,  1.0, -1.0 );  

  20.     glTexCoord2f( 0.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );  

  21.     glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0, -1.0 );  

  22.     glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0, -1.0 );  

  23.     glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0,  1.0,  1.0 );  

  24.     glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0,  1.0,  1.0 );  

  25.     glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );  

  26.     glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0, -1.0, -1.0 );  

  27.     glTexCoord2f( 0.0, 1.0 ); glVertex3f(  1.0, -1.0, -1.0 );  

  28.     glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );  

  29.     glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );  

  30.     glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0, -1.0, -1.0 );  

  31.     glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );  

  32.     glTexCoord2f( 0.0, 1.0 ); glVertex3f(  1.0,  1.0,  1.0 );  

  33.     glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );  

  34.     glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 );  

  35.     glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );  

  36.     glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0,  1.0,  1.0 );  

  37.     glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0, -1.0 );  

  38.     glEnd();  

  39.     xRot += 0.3;  

  40.       yRot += 0.2;  

  41.       zRot += 0.4;  

  42. }  




最后,在initializeGL()中加入

loadGLTextures();
 glEnable( GL_TEXTURE_2D );


编译,运行!


它确实跑起来了,不过咱们忘记了一个东西,就是纹理坐标。
纹理坐标以下图所示:

假设图中的正方向就是咱们要将纹理映射上去的物体(地面),那么咱们须要按照图中的表示,为每一个顶点指定一个纹理坐标,也称之为UV坐标,它的横向为s轴,纵向围t轴,以下图所示。关于st和uv坐标能够参考一些3D图形学相关知识。

在paintGL()中定义顶点的时候,咱们只需用glTexCoord2f()将纹理绑定到相应的顶点就能够了
相关文章
相关标签/搜索