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()將紋理綁定到相應的頂點就能夠了
相關文章
相關標籤/搜索