Texture Mapping in OpenGL (L2)

未命名Drawing 2D&3D 這篇文章裡,已經說明了如何繪製立體圖形及上色,這篇主要說明如何將圖片貼在三維空間的平面上。首先必須要先能讀取圖片檔案,我這裡是使用 QImage 存取,再者就是建立一個 texture 放入圖片資訊。圖片選擇上要注意的地方就是圖片大小,圖片長與寬的大小必須是2的冪次方( 2,4,8,16,32,64...) 再參考網頁裡,他說長寬最好大於 64 pixels 且為了兼容性(Compatability),不要大於 256 pixels(但我範例裡使用的 Lena 圖是 512x512,效果也不差,所以見仁見智囉)。

而整體來說就是多一個 loadGLTexture function,來做 texture mapping。

● Load Texture

void myopenGL::loadGLTexture(string & input_file)
{
	//@ load input_file into Qimage(img)
	QImage img, GL_img;
	if(!img.load(input_file.c_str()))
	{
		qWarning("Could not read the image file.");
		QImage dummy(512, 512, QImage::Format_RGB32);
		dummy.fill(Qt::green);
		img = dummy;
	}
	GL_img = QGLWidget::convertToGLFormat( img ); // convert QImage to GLformat

	//@ create the texture
	glGenTextures(1, &texture[0]);
	glBindTexture(GL_TEXTURE_2D, texture[0]); // bind the named texture[0]
	//@ generate the texture
	glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img.width(), GL_img.height(), 0,
				 GL_RGBA, GL_UNSIGNED_BYTE, GL_img.bits());
	//@ interoolation type for scaling
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

}

dummy 指的是如果 load image failure 時,會輸出一張綠色的圖。

未命名

QGLWidget::convertToGLFormat( img ) 指的是將 QImage format 轉成 OpenGL format,不轉的話圖片顏色會怪怪的

glGenTextures(1, &texture[0]) 產生一個 texture memory 在 texture[0] 的位置。

glBindTexture( GL_TEXTURE_2D, texture[0]) 將 texture[0] 與 GL_TEXTURE_2D 捆再一起,告訴 OpenGL 有個 texture memory 在 texture[0]。

glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img.width(), GL_img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, GL_img.bits()) 透過 GL_TEXTURE_2D 將圖片資訊存到 texture[0] 這個位置裡。glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) 函式裡 level & border 通常都設為 0;target 就是 GL_TEXTURE_2D;特別注意的是 format ,在參考網頁裡寫的是 GL_RGB,但我使用 GL_RGB 卻無法正常顯示圖形,必須使用 GL_RGBA,多個 Alpha 後才會正常顯示。

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

這兩個 functions 則是決定在圖片放大縮小時,interpolation 是要使用甚麼方式,可用 GL_LINEAR(效果好) 或 GL_NEAREST(較快)。

 

loadGLTexture(in_file);
glEnable(GL_TEXTURE_2D);

然後,只要在 initialize 裡面加入上面的 function loadGLTexture(in_file) 和啟動GL_TEXTURE_2D glEnable(GL_TEXTURE_2D)

 

glTranslatef(0.0f, 0.0f, -50.f);
glRotatef(rota_x, 1.0f, 0.0f, 0.0f);
glRotatef(rota_y, 0.0f, 1.0f, 0.0f);
glRotatef(rota_z, 0.0f, 0.0f, 1.0f);
glBindTexture( GL_TEXTURE_2D, texture[0]);	// using texture[0]
// Front Face
glBegin( GL_QUADS );
glTexCoord2f(0.0f, 0.0f); glVertex3f(-D, -D,  D);  // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( D, -D,  D);  // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( D,  D,  D);  // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-D,  D,  D);  // Top Left Of The Texture and Quad
glEnd();

最後在 draw scene 裡要注意 glTexCoord2fglVertex3f 方位要一致,不然畫出來的圖形會翻轉或顛倒;而 glBindTexture(GL_TEXTURE_2D, texture[0]) 不能放在 glBegin()glEnd() 裡面。

 

 


● Texture Filter

texture[0] 裡,我們使用的 Filter 是 GL_LINEAR,若圖片在側邊被擠壓成很細小時,圖片的內容因為 rendering 的不夠細膩而使很多細節消失;使用 GL_NEAREST Filter 情況更為糟糕。如下圖所示,六面體中,右方與下方的圖有明顯的鋸齒狀。

未命名                                                    [GL_LINEAR FILTER]

未命名2                                                [GL_NEAREST FILTER]

因此,為了使細節不因畫面壓縮而產生鋸齒狀,可以使用另一種 Filter,叫做 Mipmapped texture,說穿了他只是對 texture 做個 low pass filter ,再 rendering 到屏幕上而已,因此視覺效果會比較平順舒服,主體細節不會因為擠壓而有鋸齒狀產生。未命名3

                                               [MIPMAPPING FILTER]

	//@ texture 1
	glGenTextures(1, &texture[0]);
	glBindTexture(GL_TEXTURE_2D, texture[0]); // bind the named texture[0]
	glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img.width(), GL_img.height(), 0,
				 GL_RGBA, GL_UNSIGNED_BYTE, GL_img.bits());// 0 : iamge level usual use 0, second zero means border
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

	//@ texture 2
	glGenTextures(1, &texture[1]);
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img.width(), GL_img.height(), 0,
				  GL_RGBA, GL_UNSIGNED_BYTE, GL_img.bits());
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	//@ texture 3 MipMapped texture
	glGenTextures(1, &texture[2]);
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, GL_img.width(), GL_img.height(),
					  GL_RGBA, GL_UNSIGNED_BYTE, GL_img.bits());

 


reference : http://nehe.gamedev.net/tutorial/texture_mapping/12038/

Lena Image : http://www.cs.cmu.edu/~chuck/lennapg/lenna.shtml

 

Leave a Reply

Your email address will not be published. Required fields are marked *