紋理映射基礎知識
什麼叫紋理映射,一開始我也不明白,感覺這個詞好專業(畢竟沒有學過圖形學),後面經過網上查找資料和這次實驗稍微理解了點。紋理映射簡單的講,就是把一個紋理(其實說白了,紋理可以理解為一幅圖像)映射到空間物體的表面上,因此紋理映射也叫貼圖,這個表明不一定是矩形,比如說我可以是球面,或者是任意曲面。在上一篇文章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。
第一個參數是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){ ui->setupUi(this); fullscreen = false; rotate_angle = 0.0;}
void GLWidget::initializeGL(){ setGeometry(300, 150, 500, 500); loadTextures(); glEnable(GL_TEXTURE_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(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); 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();}
void GLWidget::resizeGL(int width, int height){ if(0 == height) height = 1; glViewport(0, 0, (GLint)width, (GLint)height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity();
}void GLWidget::keyPressEvent(QKeyEvent *e){ switch(e->key()) { case Qt::Key_F1: fullscreen = !fullscreen; if(fullscreen) showFullScreen(); else { setGeometry(300, 150, 500, 500); showNormal(); } updateGL(); break; 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); dummy.fill(Qt::green); buf = dummy; } tex = convertToGLFormat(buf); glGenTextures(1, &texture[0]); glBindTexture(GL_TEXTURE_2D, texture[0]); 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); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 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/