Lighting and Keyboard Control in OpenGL (L3)

本篇要介紹如何在屏幕裡面增加光源以及鍵盤控制。

● Lighting

想要在屏幕裡打光的話就需要定義光源的位置、顏色、強度,以及物體反光係數。OpenGL 裡主要可以設定兩種光,分別是環境光( Ambient Light ) 和 散射光( Diffuse Light ),環境光就是一般的光源,是無方向性的;散射光則是指物體本身的反光,散射的強度與物體的材質有關。除了光源的特性外,還有光的位置( Light Position )要被定義,才能決定光呈現的效果。

在 initializeGL() function 裡面,先定義好光的特性:


 GLfloat LightAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f};
 GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
 GLfloat LightPosition[] = {0.0f, 0.0f, 2.0f, 1.0f};
 //@ key control
 Light = false;
 //@ glLight 
 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
 glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
 glEnable(GL_LIGHT1);

LightAmbient[] 是定義光源的顏色強度,分別代表{ R, G, B, Alpah}。

LightDiffuse[] 則是物體表面反光強度,一樣 index 代表著 { R, G, B, Alpah}。

可以改變光源顏色與反光顏色,會有不同的效果。我上面的設定是強度最強的白光而反光係數也是最強。

LightPosition[] 前三個 index 表示光源所在的位置(x, y, z),第四個參數則是代表著定義在已訂的座標空間。

glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient)

glLightfv(GL_LIGHT1,GL_DIFFUSE, LightDiffuse)

glLightfv(GL_LIGHT1, GL_POSITION, LightPosition) 表示把剛的光源設定建議在 GL_LIGHT1 裡面。

glEnable(GL_LIGHT1) 則是啟動 GL_LIGHT1 這個光。

接下來就是如何 rendering 在屏幕上了,


 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
 glLoadIdentity();

//@ key control
 if(Light)
 glEnable(GL_LIGHTING);
 else
 glDisable(GL_LIGHTING);

 double D = 10; // size 
 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[2]); 
 
 // Front Face
 glBegin( GL_QUADS );
 glNormal3f( 0.0f, 0.0f, 1.0f); 
 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();
 //Back Face
 glBegin( GL_QUADS ); 
 glNormal3f( 0.0f, 0.0f, -1.0f); 
 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
 glTexCoord2f(0.0f, 0.0f); glVertex3f( D, -D, -D); // Bottom Left Of The Texture and Quad
 glEnd();
 // Top Face
 glBegin( GL_QUADS ); 
 glNormal3f( 0.0f, 1.0f, 0.0f); 
 glTexCoord2f(0.0f, 1.0f); glVertex3f(-D, D, -D); // Top Left Of The Texture and Quad
 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
 glEnd();
 // Bottom Face
 glBegin( GL_QUADS ); 
 glNormal3f( 0.0f,-1.0f, 0.0f); 
 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
 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
 glEnd();
 // Right face
 glBegin( GL_QUADS ); 
 glNormal3f( 1.0f, 0.0f, 0.0f); 
 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
 glTexCoord2f(0.0f, 0.0f); glVertex3f( D, -D, D); // Bottom Left Of The Texture and Quad
 glEnd();
 // Left Face
 glBegin( GL_QUADS );
 glNormal3f(-1.0f, 0.0f, 0.0f); 
 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); 
 glEnd();

glNormal3f 是定義每個面的法向量,代表每個面朝外的方位,這樣光才能正確地打在每個平面上。若不加上 glNormal3f 的話,光不會出現在正確的地方,光源會隨著你的物體旋轉而跟著旋轉。這裡要注意到,glLightfv(GL_LIGHT1, GL_POSITION, LightPosition) 一定要定義在物體做旋轉或平移之前,不然光源也會跟著物體旋轉。

 


● Keyboard Control

在 keyboard control 的部分我是套用 Qt 裡的 <QKeyEvent> widget 來實作,那這裡主要的作用就是 Lighting 的開關, L 按鍵來做開關的動作。


void QtGL::keyPressEvent(QKeyEvent * event)
{
	if(event->key() == Qt::Key_Escape)
	{
		qApp->quit();
	}
	if(event->key() == Qt::Key_L)
	{
		myqgl->Light = !myqgl->Light;
	}
    myqgl->GLupdate();
}

 

執行結果:

  1. Light 開關

picasion.com_349410d8f8b7faf61be3ab125811092d

  1. 物體旋轉下打光的變化

picasion.com_d323d25c632f98b7fde39052abac8e9a

 

 


 

reference : http://nehe.gamedev.net/tutorial/texture_filters,_lighting_&_keyboard_control/

iron image : http://www.dreamstime.com/

 

Difference Between GL_PROJECTION and GL_MODELVIEW in glMatrixMode

Opengl 裡的 矩陣模型有兩種,一個是 GL_PROJECTION,另一個是 GL_MODELVIEW,這兩個有甚麼差別呢?

PROJECTION 矩陣是定義相機本身的可視角度、視窗長寬比以及繪圖範圍(定義 clipping plane)。所以在調用 glMatrixMode(GL_PROJECTION) 後,會再呼叫 gluPerspective() 這個函式,來定義上面所述說的參數值。

void gluPerspective (
GLdouble fovy,      //可視角度
GLdouble aspect,  //視窗長寬比
GLdouble zNear,   //繪畫區域(z軸最近位置)
GLdouble zFar);    //繪畫區域(z軸最遠位置)

MODELVIEW 矩陣則是定義你的物體在做平移(glTranslatef(x,y,z))、旋轉(glRotatef(angle,x,y,z))和縮放(glScalef(x,y,z))使用。

一般情況下,PROJECTION 矩陣只會調用一次;而大部分在做矩陣旋轉平移的時候,就只會需要用到 MODELVIEW 矩陣。

 


Example

void myopenGL::resizeGL(int width, int height)
{
	if(height == 0)
		height = 1;

	glViewport(0, 0, width, height); //reset the current view port
	glMatrixMode(GL_PROJECTION); //select the projection matrix
	glLoadIdentity(); // reset the model view matrix
	WHratio = (double)width/height;
	gluPerspective(fov, WHratio, 0.1f, 100.0f); //fovy : view angle(0~180 degree), (0.1f, 100.0f) : drawing area

	glMatrixMode(GL_MODELVIEW); // select model view matrix
	glLoadIdentity();	// reset the model view matrix
}

在 resizeGL() function 裡面,GL_PROJECTION 就是針對視窗的視野角度做一次定義後,之後都交由 GL_MODELVIEW 來處理物體的型態轉換。

 

 

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 &amp;amp;amp;amp;amp;amp;amp; 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, &amp;amp;amp;amp;amp;amp;amp;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, &amp;amp;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, &amp;amp;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, &amp;amp;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

 

Drawing 2D & 3D Objects in OpenGL (L1)

Rending OpenGL Graphics in Qt 裡介紹如何定義 Qt 函式來繪製 OpenGL 物件,基本上所有物件繪製都寫在 paintGL() 這個 function 內。而今天要介紹如何繪製 2D&3D 物件、上色與物體旋轉。

● 2D OBJECT

glTranslatef(-1.5, 0.0f, -6.0f);
// Rotate the object
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);

// draw a square
glColor3f( 0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f(-1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();

glLoadIdentity();
glTranslatef(1.5f, 0.0f, -6.0f);
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);

//glTranslatef(3.0f, 0.0f, 0.0f);// move right 3 units

// draw a triagle
glBegin(GL_TRIANGLES);
glColor3f ( 0.0f, 0.0f, 1.0f);
glVertex3f( 0.0f, 1.0f, 0.0f); // x , y , z
glColor3f ( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glColor3f ( 1.0f, 0.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glEnd();

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 是清除 color 和 depth                                                                                                               buffers。

glLoadIdentity() 是指重置觀看矩陣模型,將觀看座標移至座標中心。

glTranslatef(x, y, z) 將物體沿著各軸移動;x 指左右移動(左負右正)、y 指上下移動(上正                                   下負)、z 指移入移出屏幕(移出正 移入負)

glRotatef( angle, x, y, z) 可以針對不同向量(x,y,z)做 angle 角度的旋轉,旋轉方向符合右                                           手定則

glColor3f(R,G,B) RGB 為三個 channel 的數值,範圍 [0.0 ~1.0]。

glBegin(object type) 表示開始繪畫物件,() 裡的 object type 代表繪畫的樣式,                                                    GL_QUADS 表示繪畫方形,GL_TRANGLES 表示三角形。

glVertex3f(x, y, z) 標記點,(x,y,z) 代表在空間中的座標點位置。

glEnd() 表示繪圖結束,與glBegin() 是一對的。

執行結果(rota_x = 0, rota_y = 10, rota_z = 0):

未命名

 

 

● 3D OBJECT

glTranslated(-1.5f, 0.0f,-6.0f);
glRotatef(15, 1.0f, 0.0f, 0.0f);
glRotatef(0, 0.0f, 1.0f, 0.0f);
glRotatef(0, 0.0f, 0.0f, 1.0f);

glBegin(GL_TRIANGLES);
// front
glColor3f(  1.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(  0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glColor3f(  0.0f, 0.0f, 1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
// right
glColor3f(  1.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(  0.0f, 0.0f, 1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(  0.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
// left
glColor3f(  1.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(  0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(  0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f,-1.0f,1.0f);
//back
glColor3f(  1.0f, 1.0f, 1.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(  0.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
glColor3f(  0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glEnd();

glLoadIdentity();
glTranslatef(1.5f, 0.0f, -7.0f);
glRotatef(20, 1.0f, 0.0f, 0.0f);
glRotatef(0, 0.0f, 1.0f, 0.0f);
glRotatef(0, 0.0f, 0.0f, 1.0f);

glBegin(GL_QUADS);
//top
glColor3f(  1.0, 0.0, 0.0);
glVertex3f(-1.0, 1.0,-1.0);
glVertex3f( 1.0, 1.0,-1.0);
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0);
//front
glColor3f(  0.0, 1.0, 0.0);
glVertex3f(-1.0, 1.0,-1.0);
glVertex3f(-1.0,-1.0,-1.0);
glVertex3f( 1.0,-1.0,-1.0);
glVertex3f( 1.0, 1.0,-1.0);
//right
glColor3f(  0.0, 1.0, 1.0);
glVertex3f( 1.0, 1.0,-1.0);
glVertex3f( 1.0,-1.0,-1.0);
glVertex3f( 1.0,-1.0, 1.0);
glVertex3f( 1.0, 1.0, 1.0);
//back
glColor3f(  0.0, 0.0, 1.0);
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f( 1.0,-1.0, 1.0);
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0);
//left
glColor3f(  1.0, 1.0, 0.0);
glVertex3f(-1.0, 1.0,-1.0);
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f(-1.0,-1.0,-1.0);
//bottom
glColor3f(  1.0, 0.0, 1.0);
glVertex3f( 1.0,-1.0,-1.0);
glVertex3f( 1.0,-1.0, 1.0);
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f(-1.0,-1.0,-1.0);
glEnd();

3D 物件繪畫方式與 2D 相同,只是 2D 的延伸。例如:一個立方體有六面,那就表示要畫六次 2D 平面;然而三角錐有四面,就是要畫四個平面。唯一要注意的地方就是,在繪畫任何一個方體的時候,每一面的繪畫點一定要都是順時針繞或逆時針繞,不能一面用順時針標點,另一面用逆時針標點,這樣 OpenGL 是判別不出來的,所以每一面一定要同個方向繞點。 執行結果:

未命名

 

 


reference : http://nehe.gamedev.net/tutorial/lessons_01__05/22004/

 

Rendering OpenGL Graphics in Qt

若今天不使用 GLUT 的視窗介面而改用 Qt 來 rendering (渲染) 3D 圖形時,則要藉由 QGLWidget 這個 Class 來實現。

QGLWidget 提供三個非常方便的虛擬函式( Virtual Function) ,分別是 initializeGL()resizeGL() paintGL()。所以在衍生類別的 function naming 要與上述的三個 function 相符合,不然無法 rendering OpenGL 的圖形。

initializeGL() :: 初始設定 rendering 的預設參數,在第一次使用 resizeGL() 和 paintGL() 之前,會先呼叫 initializeGL() 。執行順序是 initializeGL() --> resizeGL() --> paintGL() 。

resizeGL() :: 設定 OpenGL 的觀看位置、矩陣型態、視野角度以及 OpenGL drawing 的範圍...等等。除了第一次 widget 被 create 時會被呼叫,之後只要 widget 有被 resized 時都會重新被呼叫。

paintGL() :: Rendering OpenGL 的畫面,你所畫的物件都應該在這個 function 裡定義。

 


** Example **

class myQGL : public QGLWidget
{
	Q_OBJECT

public:
	myQGL(QWidget *parent = 0);
	~myQGL();

	// &gt;&gt;&gt; QGLWidget protected funs
	virtual void initializeGL();
	virtual void paintGL();
	virtual void resizeGL(int width, int height);
	// &lt;&lt;&lt; QGLWidget portected funs

	public slots:
		void GLupdate();
private:
};
void myQGL::initializeGL()
{
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // background ( R, G, B, Alpha)
	glClearDepth(1.0f);	//depth buffer setup
	glShadeModel(GL_SMOOTH); // enables smooth shading
	glEnable(GL_DEPTH_TEST); // enable depth testing
	glDepthFunc(GL_LEQUAL); // the type of depth test to do

	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

void myQGL::resizeGL(int width, int height)
{
	if(height == 0)
		height = 1;

	glViewport(0, 0, width, height); //reset the current view port
	glMatrixMode(GL_PROJECTION); //select the model view matrix
	glLoadIdentity(); // reset the model view matrix 

	//calculate the aspect ratio of the window
	gluPerspective(45, (GLfloat)width/height, 0.1f, 100.0f); //fovy : view angle, (0.1f, 100.0f) : drawing area
	glMatrixMode(GL_MODELVIEW); // select model view matrix
	glLoadIdentity();	// reset the model view matrix

}

void myQGL::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glTranslatef(-1.5, 0.0f, -6.0f);

	// draw a triagle
	glBegin(GL_TRIANGLES);
	glColor3f( 0.0f, 0.0f, 1.0f);
	glVertex3f( 0.0f, 1.0f, 0.0f); // x , y , z
	glColor3f( 0.0f, 1.0f, 0.0f);
	glVertex3f(-1.0f,-1.0f, 0.0f);
	glColor3f( 1.0f, 0.0f, 0.0f);
	glVertex3f( 1.0f,-1.0f, 0.0f);
	glEnd();
	glTranslatef(3.0f, 0.0f, 0.0f);// move right 3 units

	// draw a square
	glColor3f( 1.0f, 1.0f, 0.0f);
	glBegin(GL_QUADS);
	glVertex3f(-1.0f, 1.0f, 0.0f);
	glVertex3f( 1.0f, 1.0f, 0.0f);
	glVertex3f( 1.0f,-1.0f, 0.0f);
	glVertex3f(-1.0f,-1.0f, 0.0f);
	glEnd();
}

執行結果

triangle & squad                                                      [ Triangle and Square ]


 

reference : http://doc.qt.io/qt-4.8/qglwidget.html#details

OpenGL Utility Toolkit (GLUT)

OpenGL 是非常普遍的 2D3D 圖像函式庫,開發者可藉由 OpenGL 撰寫 2D 與 3D 的繪圖程式,且支援多種程式語言,C/C++、C#、Java、Python...等。基本上 Visual Studio 都已經把 gl.h 和 glu.h 給包在裡面了,所以只要到 OpenGL 官網再安裝 GLUT 就可以使用了。

OpenGL環境設定

  1. Download : http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip
  2. 將 glut.dll 和 glut32.dll 放到 C:\WINDOWS\system32 目錄下。
  3. 將 glut32.lib 和 glut.lib 放到C:\Program Files\Microsoft Visual Studio 9.0\VC\lib目錄下。
  4. 將 glut.h 放到C:\Program Files\Microsoft Visual Studio 9.0\VC\ Include\GL\目錄下
  5. Download GLEW file : http://glew.sourceforge.net/,glew 是提供更多 library 使用,像是支援cube map texture 的 function 使用。
  6. 將 glew.h、glxew.h 和 wglew.h 放到C:\Program Files\Microsoft Visual Studio 9.0\VC\ Include\GL\目錄下。
  7. 在 project 的屬性/連結器/輸入 加入 glut.lib 和 glut32.lib。

gl.h :: 是主要的檔案,包含所有基本的函式,用到 gl 開頭的 function 都是來自於 gl.h。

glu.h :: 則是 gl.h 的輔助,提供更多 function 使用。

glut.h :: 是個獨立的視窗介面,像是 MFC 或 Qt 用來編寫視窗的 library。

glaux.h :: 與 glew.h 一樣,擴增新的函式使用,但只 for windows。

 

 


reference : http://www.cc.ntu.edu.tw/chinese/epaper/0024/20130320_2410.html

 

Cube Texture Mapping

 

Cube Mapping是一種將周圍環境投影到無限大且無死角的360°平面上,就像天空一般。所謂Cube Map即是六張不同的2D影像,利用opengl將這六張影像投影到一個Cube上。Load Texture方式有兩種,一種是最簡單的2D Texture Mapping (GL_TEXTURE_2D);另一種是GL_TEXTURE_CUBE_MAP,使用GL_TEXTURE_CUBE_MAP必須include GL/glew.h這個head檔才能使用,GL_TEXTURE_CUBE_MAP已經將Cube面向給定義好了,只要依循下列位置擺放六張圖就可以了。

GL_TEXTURE_CUBE_MAP


OpenGL環境設定

  1. Download : http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip
  2. 將glut32.dll放到C:\WINDOWS\system32目錄下
  3. 將glut32.lib和opengl32.lib放到C:\Program Files\Microsoft Visual Studio 9.0\VC\lib目錄下
  4. 將glut.h放到C:\Program Files\Microsoft Visual Studio 9.0\VC\ Include\GL\目錄下
  5. Download GLEW file : http://glew.sourceforge.net/
  6. 將glew.h、glxew.h和wglew.h放到C:\Program Files\Microsoft Visual Studio 9.0\VC\ Include\GL\目錄下

code::GL_TEXTURE_2D Texture Mapping

// FRONT
 if ( !buf.load( filename.c_str() ) ){
qWarning( "Could not read image file, using single-color instead." );
QImage dummy( 128, 128,QImage::Format_RGB32);
dummy.fill( Qt::green);
buf = dummy;}
tex = QGLWidget::convertToGLFormat( buf );
glGenTextures( 1, &amp;amp;amp;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 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // for texture seamless from glew.h
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // for texture seamless from glew.h
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // for texture seamless from glew.h
// BACK
filename = folder + "BACK" + subfile;
buf.load(filename.c_str());
tex = QGLWidget::convertToGLFormat( buf );
glGenTextures( 1, &amp;amp;amp;texture[1] ); 
glBindTexture( GL_TEXTURE_2D, texture[1] );
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 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // for texture seamless from glew.h
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // for texture seamless from glew.h
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // for texture seamless from glew.h
// TOP
filename = folder + "TOP" + subfile;
buf.load(filename.c_str());
tex = QGLWidget::convertToGLFormat( buf );
glGenTextures( 1, &amp;amp;amp;texture[2] ); 
glBindTexture( GL_TEXTURE_2D, texture[2] );
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 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// DOWN
filename = folder + "DOWN" + subfile;
buf.load(filename.c_str());
tex = QGLWidget::convertToGLFormat( buf );
glGenTextures( 1, &amp;amp;amp;texture[3] ); 
glBindTexture( GL_TEXTURE_2D, texture[3] );
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 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// RIGHT
filename = folder + "RIGHT" + subfile;
buf.load(filename.c_str());
tex = QGLWidget::convertToGLFormat( buf );
glGenTextures( 1, &amp;amp;amp;texture[4] );
glBindTexture( GL_TEXTURE_2D, texture[4] );
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 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// LEFT
filename = folder + "LEFT" + subfile;
buf.load(filename.c_str());
tex = QGLWidget::convertToGLFormat( buf );
glGenTextures( 1, &amp;amp;amp;texture[5] );
glBindTexture( GL_TEXTURE_2D, texture[5] );
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 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

code::GL_TEXTURE_CUBE_MAP Texture Mapping

// FRONT
 if ( !buf.load( filename.c_str() ) ) 
 {
 qWarning( "Could not read image file, using single-color instead." );
 QImage dummy( 128, 128,QImage::Format_RGB32);
 dummy.fill( Qt::green);
 buf = dummy;
 }
 tex = QGLWidget::convertToGLFormat( buf );
 glGenTextures( 1, &amp;amp;amp;texture[0] ); 
 glBindTexture( GL_TEXTURE_CUBE_MAP, texture[0] );
 glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 3, tex.width(), tex.height(), 0,
 GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
 // BACK
 filename = folder + "LEFT" + subfile;
 buf.load(filename.c_str());
 tex = QGLWidget::convertToGLFormat( buf );
 glTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, 3, tex.width(), tex.height(), 0,
 GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );

// TOP
 filename = folder + "TOP" + subfile;
 buf.load(filename.c_str());
 tex = QGLWidget::convertToGLFormat( buf );
 glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, 3, tex.width(), tex.height(), 0,
 GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );

// DOWN
 filename = folder + "DOWN" + subfile;
 buf.load(filename.c_str());
 tex = QGLWidget::convertToGLFormat( buf );
 glTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, 3, tex.width(), tex.height(), 0,
 GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );

// RIGHT
 filename = folder + "BACK" + subfile;
 buf.load(filename.c_str());
 tex = QGLWidget::convertToGLFormat( buf );
 glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, 3, tex.width(), tex.height(), 0,
 GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );

// LEFT
 filename = folder + "FRONT" + subfile;
 buf.load(filename.c_str());
 tex = QGLWidget::convertToGLFormat( buf );
 glTexImage2D( GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, 3, tex.width(), tex.height(), 0,
 GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );

// Texture Filter
 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

Textures :

cube

Cube Texture Mapping 結果:

Cube

 


 

reference :

http://learnopengl.com/#!Advanced-OpenGL/Cubemaps

http://www.braynzarsoft.net/index.php?p=D3D11CUBEMAP