OpenGL_Qt學習筆記之_05(紋理映射)


     

  紋理映射基礎知識

  什么叫紋理映射,一開始我也不明白,感覺這個詞好專業(畢竟沒有學過圖形學),后面經過網上查找資料和這次實驗稍微理解了點。紋理映射簡單的講,就是把一個紋理(其實說白了,紋理可以理解為一幅圖像)映射到空間物體的表面上,因此紋理映射也叫貼圖,這個表明不一定是矩形,比如說我可以是球面,或者是任意曲面。在上一篇文章OpenGL_Qt學習筆記之_04(3D圖形的繪制和旋轉)中,我們繪制的空間體的表面都是一些光滑的顏色,如果要在其表面采用那種方法繪制圖像的話,則必須利用微分的思想,一個一個的繪制小圖像,然后拼接起來,可想而知,這個過程是多么的復雜,opengl的紋理映射就可以很好的解決這一問題。

     下面來看看紋理映射的一個示意圖片(百度百科上的):

   

  這個示意圖說明,將中間的紋理(即圖片)映射到左邊的茶壺曲面上,就形成了右邊的圖了。在右邊的圖中,可以看到茶壺的表面上布滿了圖片,這就像中國古代的陶器繪圖一樣。

  紋理映射另外一個好處是能夠保證在變換多邊形時,多邊形上的紋理也會隨之變化。

 

  紋理映射相關函數

  要進行紋理映射,得先了解下opengl中有關紋理映射的一些相關函數。

  void glGenTextures(GLsizei n, GLuint *textures);

  該函數的作用是開辟存儲紋理的內存空間,其中參數n為開辟紋理內存的個數,texture為存儲紋理的地址索引。

 

  void glBindTexture(GLenum target,   GLuint texture);

  該函數的作用是把存儲紋理的對象texture綁定到紋理目標target上,在opengl中紋理目標分為GL_TEXTURE_1D和GL_TEXTURE_2D。該句代碼運行完后,對target的操作也對應於對texture指向的內容的操作。

 

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

  參數1為紋理目標;參數2為目標的層次,即目標的詳細程度,一般情況采用0即可;參數3表示的是數據成分的個數,如果數據由RGB構成,則將該參數設置為3;參數4和5分別為創建紋理數據的長和寬;參數6為邊框的值,一般也設為0;參數8為數據的通道格式;參數9為紋理的數據元素類型;參數10為紋理的數據內容。

  這個函數的功能是創建一個紋理,並為該紋理分配了數據。

 

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

  該函數表示的是當所顯示的紋理比加載進來的紋理小時,采用GL_LINEAR的方法來處理。

 

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  和上面的函數類似,注意該函數的參數2為GL_TEXTURE_MAG_FILTER,不要誤認為是GL_TEXTURE_MAG_FILTER。

 

  glTexCoord2f(x, y);

  第一個參數是X坐標。 0.0f 是紋理的左側。 0.5f 是紋理的中點, 1.0f 是紋理的右側。第二個參數是Y坐標。0.0f 是紋理的底部。0.5f 是紋理的中點,1.0f 是紋理的頂部。

 

  給定一張紋理圖,它的坐標示意圖如下:

   

  其中紋理的中心點坐標為(0.5, 0.5).

 

  實驗說明:

  這個實驗室在上一篇博文OpenGL_Qt學習筆記之_04(3D圖形的繪制和旋轉) 繪制的立方體上貼每個面上貼上一張紋理(即圖片)。

  我們需要在GLWidget這個類中添加一個函數來加載紋理數據,該函數為loadTextures().

  另外,在QGLWidget這個類中,initializeGL(), paintGL(), resizeGL()這3個函數的執行順序是該類啟動時就執行initializeGL(),主要用於初始化opengl,通常用來設置一些前期的背景色,光照參數等等。paintGL()用於渲染整個場景;resizeGL()用於在widget大小變化的時候產生合理view。

  注意,本實驗給的圖片長和寬必須是2的n次方,最大不要超過256,最小也不要小於64。我這里采用的是256*256像素的圖片。

 

  實驗結果:

  加載紋理的圖像為:

  

 

  紋理映射后的效果如下:

   

 

  實驗主要部分代碼及注釋(附錄有工程code下載地址):

#include "glwidget.h"
#include "ui_glwidget.h"

#include <QtGui>
#include <QtCore>
#include <QtOpenGL>

GLWidget::GLWidget(QGLWidget *parent) :
    QGLWidget(parent),
    ui(new Ui::GLWidget)
{
  //  setCaption("The Opengl for Qt Framework");
    ui->setupUi(this);
    fullscreen = false;
    rotate_angle = 0.0;
}

//這是對虛函數,這里是重寫該函數
void GLWidget::initializeGL()
{
    setGeometry(300, 150, 500, 500);//設置窗口初始位置和大小
    loadTextures();
    glEnable(GL_TEXTURE_2D);//允許采用2D紋理技術
    glShadeModel(GL_SMOOTH);//設置陰影平滑模式
    glClearColor(0.0, 0.0, 0.0, 0);//改變窗口的背景顏色,不過我這里貌似設置后並沒有什么效果
    glClearDepth(1.0);//設置深度緩存
    glEnable(GL_DEPTH_TEST);//允許深度測試
    glDepthFunc(GL_LEQUAL);//設置深度測試類型
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//進行透視校正
}

void GLWidget::paintGL()
{
    //glClear()函數在這里就是對initializeGL()函數中設置的顏色和緩存深度等起作用
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /*下面開始畫立方體,並對其進行紋理映射*/
    glLoadIdentity();
    glRotatef(rotate_angle, -0.2f, 0.2f, -0.3f);
    glBegin(GL_QUADS);
    //上頂面
    glTexCoord2f(0.0, 1.0);//將2D的紋理坐標映射到3D的空間物體表面上
    glVertex3f(-0.3f, 0.3f, -0.3f);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-0.3f, 0.3f, 0.3f);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(0.3f, 0.3f, 0.3f);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(0.3f, 0.3f, -0.3f);
    //下頂面
    glTexCoord2f(0.0, 1.0);
    glVertex3f(-0.3f, -0.3f, -0.3f);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-0.3f, -0.3f, 0.3f);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(0.3f, -0.3f, 0.3f);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(0.3f, -0.3f, -0.3f);
    //正前面
    glTexCoord2f(0.0, 1.0);
    glVertex3f(-0.3f, 0.3f, 0.3f);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-0.3f, -0.3f, 0.3f);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(0.3f, -0.3f, 0.3f);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(0.3f, 0.3f, 0.3f);
    //右側面
    glTexCoord2f(0.0, 1.0);
    glVertex3f(0.3f, 0.3f, 0.3f);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(0.3f, -0.3f, 0.3f);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(0.3f, -0.3f, -0.3f);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(0.3f, 0.3f, -0.3f);
    //背后面
    glTexCoord2f(0.0, 1.0);
    glVertex3f(-0.3f, 0.3f, -0.3f);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(0.3f, 0.3f, -0.3f);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(0.3f, -0.3f, -0.3f);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(-0.3f, -0.3f, -0.3f);
    //左側面
    glTexCoord2f(0.0, 1.0);
    glVertex3f(-0.3f, 0.3f, -0.3f);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-0.3f, -0.3f, -0.3f);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(-0.3f, -0.3f, 0.3f);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(-0.3f, 0.3f, 0.3f);
    rotate_angle -= 3;
    glEnd();
}

//該程序是設置opengl場景透視圖,程序中至少被執行一次(程序啟動時).
void GLWidget::resizeGL(int width, int height)
{
    if(0 == height)
        height = 1;//防止一條邊為0
    glViewport(0, 0, (GLint)width, (GLint)height);//重置當前視口,本身不是重置窗口的,只不過是這里被Qt給封裝好了
    glMatrixMode(GL_PROJECTION);//選擇投影矩陣
    glLoadIdentity();//重置選擇好的投影矩陣
   // gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);//建立透視投影矩陣
    glMatrixMode(GL_MODELVIEW);//以下2句和上面出現的解釋一樣
    glLoadIdentity();

}
void GLWidget::keyPressEvent(QKeyEvent *e)
{
    switch(e->key())
    {
        //F1鍵為全屏和普通屏顯示切換鍵
        case Qt::Key_F1:
            fullscreen = !fullscreen;
            if(fullscreen)
                showFullScreen();
            else
            {
                setGeometry(300, 150, 500, 500);
                showNormal();
            }
            updateGL();
            break;
        //Ese為退出程序鍵
        case Qt::Key_Escape:
            close();
    }
}

/*裝載紋理*/
void GLWidget::loadTextures()
{
    QImage tex, buf;
    if(!buf.load("../opengl_nehe_05/cherry.jpg"))
    {
        qWarning("Cannot open the image...");
        QImage dummy(128, 128, QImage::Format_RGB32);//當沒找到所需打開的圖片時,創建一副128*128大小,深度為32位的位圖
        dummy.fill(Qt::green);
        buf = dummy;
    }
    tex = convertToGLFormat(buf);//將Qt圖片的格式buf轉換成opengl的圖片格式tex
    glGenTextures(1, &texture[0]);//開辟一個紋理內存,內存指向texture[0]
    glBindTexture(GL_TEXTURE_2D, texture[0]);//將創建的紋理內存指向的內容綁定到紋理對象GL_TEXTURE_2D上,經過這句代碼后,以后對
                                            //GL_TEXTURE_2D的操作的任何操作都同時對應與它所綁定的紋理對象
    glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());//開始真正創建紋理數據
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//當所顯示的紋理比加載進來的紋理小時,采用GL_LINEAR的方法來處理
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//當所顯示的紋理比加載進來的紋理大時,采用GL_LINEAR的方法來處理
}

GLWidget::~GLWidget()
{
    delete ui;
}

 

 

  總結:

  了解了opengl中的紋理映射機制后,對空間體的表面進行貼圖還是比較方便的。

 

   參考資料:

   http://nehe.gamedev.net/ 

   http://www.owlei.com/DancingWind/

   http://blog.csdn.net/qp120291570/article/details/7853513

     http://www.qiliang.net/old/nehe_qt/

 

  附錄:

  實驗工程code下載

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM