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

 

日本獼猴(Macaca fuscata)

最近在 knowledge 雜誌上報導『愛泡溫泉的猴子,日本獼猴』和 Discovery 上對日本獼猴的介紹,再加上上次在嵐山看到日本獼猴乖巧得令人印象深刻,所以特別來了解這群除了人類外,日本獼猴是生活在最北邊的靈長類動物,又稱雪猴。此外,獼猴也是除了人類外,分布最廣數量最多的靈長類。

日本獼猴身上覆蓋厚厚的毛髮,不像人類有這麼多的汗腺會散失體溫,但在日本寒冬低溫能夠降至攝氏零下20度,溫泉能讓嚴寒中的猴子們得到溫暖,但泡溫泉這個行為並非是從久遠以前就流傳下來的,是在1960年代初期,有一隻母猴為了撿拾大豆率先跳入長野縣山區的溫泉裡,意外發現這溫暖的池水,從此之後,泡溫泉就成為雪猴的習俗了。近期 Discovery 也有拍攝到日本獼猴為了撿拾溫泉裡的食物,會潛下水憋氣一分鐘左右,這也是近期才發現的習性,對於學習與演化,獼猴們的能力是不可小覷的,或許哪天真的可以看到猩球崛起裡的猴子開始學習人類的事物也不一定。

觀看獼猴泡溫泉最有名的地方就是『地獄谷野猿公苑』,位於長野縣的橫湯川河谷,地獄谷一年之中有三分之一的時間都覆蓋著白雪,獼猴已經在這居住數千年之久,之所以稱之為地獄谷是因為這裡的懸崖峭壁與地熱湧泉的特殊地理環境下才會有地獄之名。

日本獼猴與台灣獼猴一樣,都屬於母系社會,母猴留在原生家族哩,繼承母親的位階,繁衍後代,而公猴則在六七歲時,達到性成熟後就會離開原本猴群,獨自活動或加入其他猴群行列裡。每隻母猴一生平均會生10隻寶寶,每兩年生育一次,懷孕期約180天(五至六個月)。

猴群之間的『理毛』,不是為了抓對方身上的寄生蟲,而是攝取皮膚汗腺排出來的結晶鹽,但更重要的是『理毛』這種行為更是象徵一種社交意義,能夠讓你幫我理毛,是出自於我對你的認同與信任,血緣不深的猴子,是不太會有理毛行為發生的。

 


reference : BBC knowledge 雜誌

images : http://www.shahrogersphotography.com/ 。照片是來自一對熱愛生態攝影的夫妻,專門拍攝非洲與亞洲靈長類。

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/

 

Nikon D750 ! GOT IT

終於升上全福機拉~~~ D90 也用了五年多,當初花了快三萬塊買 D90,現在二手價只剩六千塊了...不過機身每年推陳出新的速度本來就很快,大幅跌價是無法避免的。

趁著這次 2015 台北國際攝影器材展,看看有沒有優惠的價格。後來在 Nikon 某一攤位問到 D750 單機身只要四萬五,比億華水貨還要便宜阿(億華水貨要 46700)!猶豫了一天,在展期的最後一天還是把它抱回家啦!IMG_3859-2不僅只是價格較便宜,這次國祥也送了一些贈品,再加上我厚臉皮不斷要求業務送東西。這次花四萬五總共獲得以下物品:

  1. D750 公司貨
  2. 原廠電池 x1
  3. 32g SD 卡 + 16g SD 卡
  4. TRIPOD 腳架 x1
  5. Kanera 小包包 x2
  6. 清潔組 x1
  7. Nikon 衣服 x1
  8. Nikon 手環 x2
  9. Nikon 字樣黑卡 x1
  10. I AM NIKON 鏡頭蓋與屁股蓋 x1
  11. Nikon School 優惠券

贈品真的超多的拉!雖然很多都不實用,但能拿就多要一些,我也多拿了幾個 nikon 提袋,讓這次消費價值最大化。

IMG_3862-2IMG_3860IMG_3861IMG_3863最後附上 D750 + 17-55 的合體照。FX 加上 DX 鏡頭無法完全解放全福機,現在要開始煩惱要繼續使用 17-55 F2.8 還是換 24-120 F4 了,兩個各有優缺。以上是非常簡陋的開箱文,照片是用 iphone 不負責亂拍...
IMG_3866

 


2015/11/11 updata

IMG_3934

今天全幅套裝正式完備!出掉 17-55 F2.8 DX,換來 24-120mm F4,掛上 B+W MRC UV 010 Haze 保護鏡,再換了一個 nikon 減壓背帶,整個霸氣沖天!猶如賽亞人進段變身一般。未來只要再購入 nikkor 20mm F1.8,補足 24-120 廣角變形的缺陷。

 

嵐山猴子公園(Arashiyama Monkey Park)

來京都除了看寺廟與神社,或者到奈良摸摸小鹿療癒療癒,也可以來嵐山餵餵猴子~~~_D8E1974

一直以為嵐山不就只有楓葉、小火車和渡月橋,沒想到還有如此療癒的地方,一知道嵐山有獼猴可以看,馬上排進行程裡。

嵐山猴子公園的猴子是日本獼猴,又稱雪猴。跟台灣獼猴都屬於馬來猴系,體型與毛色很像,但可以從臉部與尾巴長度來判斷是日本獼猴還是台灣獼猴。日本獼猴臉比較紅,尾巴比較短,尾巴長度約只有10公分,是馬來猴系裡面尾巴最短的獼猴種;台灣獼猴則是臉比較扁且尾巴粗長。而且日本獼猴善於游泳喔(和泡溫泉),而且能夠潛在水裡一分鐘以上。

_D8E2038 Formosan_macaque                                            [日本獼猴(左圖);台灣獼猴(右圖)]

來嵐山猴子公園有個非常嚴格的規定,就是不能在戶外餵食猴子,唯一能餵猴子的地方就是山頂上的屋子裡。_D8E1906

山頂上有個屋子,遊客只能進入屋內,透過鐵網來餵食,這有點違反我們的刻板印象,應該是猴子在籠子裡,我們拿食物餵食他們。但日本是以猴子為主角,讓猴子們自由自在生活在這座山裡,唯一讓人類與猴子互動的地方就限制在山頂的屋子裡,猴子們就會透過鐵網來跟你要食物吃,而屋內也會賣花生和水果,供遊客餵食。

_D8E1992 _D8E2010

_D8E1946                                                        [伸手要食物的獼猴]

_D8E2080                                                   [深情款款的需要你餵食]

獼猴要食物太可愛了,在屋內我應該有拍上百張吧... 也可以到屋外看看,在戶外就要與獼猴保持一到兩公尺的距離,距離他太近,他也會露出尖牙利嘴的樣子來威嚇你,但其實他們也很習慣有人類在旁邊,只要不要靠太近,他們基本上是不太會理你的。

_D8E2038 _D8E2033_D8E1907                                                  [注意獼猴威嚇的表情]

 


● 交通與位置

未命名只要過了渡月橋右轉後,就可以看到入園的招牌了。

_D8E2086

再走上一道階梯後,可以看到左前方就是售票口。

_D8E1899

可以注意一下入園時間,從 9:00 AM 到 5:30 PM,但售票時間只到 5:00 PM,因為要看獼猴要走到山頂上,約20分鐘,別小看這20分鐘,走起來還滿累人的。_D8E1903 _D8E1900

 

 


日本人在野生動物管理上真的很有一套,奈良梅花鹿、嵐山雪猴、藏王狐狸村和大久野島(兔島),都很成功的讓動物成為當地主角來吸引遊客來光觀。反觀台灣,動物是養一堆,但最後都變成流浪貓狗,中山大學後山的台灣獼猴也缺乏管理,變成校區裡的強盜,台灣在動物上真的缺乏有效的管理,如果地方政府能夠適時地介入,讓當地成為特有的動物園區,一定比建設一堆蚊子館來的更有光觀效益。

看完嵐山猴子後真的有被療癒的感覺,適時地跳脫平淡規律的日常生活來與其他生物互動,是可以獲得很多喜悅的。讓除了人類以外的生物融入你的生活裡,即便只是一隻螞蟻或蜘蛛,透過觀察與互動,會得到心靈與知覺上的多重愉悅,就像何曼莊在大動物園裡面說講的,有些事情是可以馬上做到,例如:去探望離家最近的活長頸鹿吧。

 

 


reference : http://web2.nmns.edu.tw/Exhibits/93/monkey/in-1-6.htm

 

奈良黃金雞排山 ( とんまさ )

_D8E1773

在旅遊書上無意間看到這間餐廳名叫『とんまさ 』,裡面有賣驚人的巨無霸黃金雞排飯。以前看日本大胃王節目的時候,就非常想來挑戰看看各種巨無霸美食了!所以趁著這次去奈良玩,就把黃金雞排山給排入當天的晚餐,也是這次旅遊最期待的一餐阿~~~

_D8E1779

とんまさ 位於大和郡山,距離最近的地鐵站是近鐵郡山站,店面小小的不太起眼,但其實還未到店門口,炸雞排的香氣四溢就足以吸引饕客上門了。

_D8E1765_D8E1767

一進店了就會看到牆壁上貼滿雜誌與電視所報導的字報,雖然看不懂,但對於滿牆的被報導紀錄就覺得這餐一定沒有白來( 餐廳位置真的有點給他偏僻... )

_D8E1766可能餐廳位置太過偏僻,七點多到,店裡幾乎都是日本人,感覺沒有其他觀光客來用餐。店裡的 menu 都是日文沒有圖片...所以只能用手機照片來點餐了。

_D8E1773_D8E1771 這份雞排丼真的太驚人了,服務生送上來的時候,還要用手壓著上端的雞排,以免雞排山走山... 送上來瞬間真的是唾液是急速分泌,讓人食指大動。就我算過這大概是一般黃金雞排五倍之多(頂端兩份,側邊三份),這份黃金雞排飯價格是日幣1700,約台幣五百元。_D8E1775雞排入油鍋之前有先用蛋汁裹衣,整體口感偏脆,份量真的很多,我吃了一半就吃不下了... 中間還有密集的高麗菜絲,這根本就不可能吃得完阿... 但坐在我後面的一位媽媽也點黃金雞排山,比我晚上菜,卻已經快吃完了... 

_D8E1777 _D8E1778

店裡除了黃金雞排外,還有很多可以選擇,價格約在日幣1500~2500上下,就我觀察雞排炸蝦雙拼也滿多人點的,看起來也很好吃。


● 交通

如果是從奈良公園到大和郡山的話,可以坐近鐵奈良線,從近鐵奈良站到大和西大寺站,再轉近鐵橿原線坐到近鐵郡山站。近鐵奈良站 >> (近鐵奈良線) >> 大和西大寺站 >> (近鐵橿原線) >> 近鐵郡山站

_D8E1780到了近鐵郡山站後,還要向西走過兩個紅綠燈,路口再左轉,約八百公尺後才會到 とんまさ。這條路上都沒有路燈,小心雙向來車。

未命名

 

 


題外話,我用 google 翻譯 とんまさ,發現意思怎麼會是『的白痴』....

未命名

 

 


官網 : http://www.tonmasa.com/

 

 

鏡頭膠皮清潔方法

無論是相機還是鏡頭,用久了蒙皮或膠皮都會白化鬆落,特別是鏡頭,汗漬會卡在對焦環與變焦環的溝槽裡面,不特別去清理的話,久了就會溝槽裡就會出現很明顯的白化汗漬。IMG_3639                                                [ 鏡頭膠皮溝槽裡的汗漬 ]

上網看到網友說可以用碧麗珠皮革清潔劑或者是車用胎蠟沾布擦拭。今天就去特力屋買了一瓶 3M 的『雙效皮革乳液』來清潔膠皮裡的汗漬,噴出來的是白色泡沫狀的液態乳液,個人覺得不會太油,可以直接噴在布上再做擦拭。IMG_3643                                                     [ 3M 雙效皮革乳液 ]

基本上我是先噴少量在眼鏡布上面,用眼鏡布的好處是表面光滑且不會掉毛屑。表面清潔很簡單,只要輕輕擦拭過,鏡頭就會黑得發亮了。至於溝槽清潔方法,我分享我的兩種擦拭方法:1. 將眼鏡布覆蓋住手指頭,用手指甲輕輕的延著溝槽擦拭;2.輕握眼鏡布,讓沾上皮革油的眼鏡布塞滿溝槽,再沿著溝槽擦拭即可。兩種方法都好用,但第一種方法,溝槽邊緣的汗漬就比較難擦到。

IMG_3640IMG_3642

擦拭完後,明顯黑亮很多!!!對這罐 3M 雙效皮革乳液很滿意,因為不僅僅可以保養鏡頭膠皮,平常還可以擦拭車子的皮椅或家裡的沙發。IMG_3641                                            [ 擦拭後,黑亮得像新鏡頭一般 ]

題外話,上星期我有一顆 Nikon 16-85 因為超音波對焦壞掉,拿去億華修,維修價格是三千;但那顆鏡頭變焦環的膠皮有點鬆落,所以問億華維修員加換膠皮價格是多少,他回我說原價九百算我七百。所以如果只是膠皮白化,還沒到鬆落或裂開的話,可以自己買皮革油來擦拭,等鬆落再花個幾百塊換一條新的膠皮就好了,但平常還是要勤於保養小心使用才是治本之道阿~

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