Sphere Mapping in OpenGL (L12)

2016-03-10_135900

Sphere Environment Mapping 是個可以創造出反射環境的金屬幾何體或者是反射屏幕內容到二次曲面上。雖然本篇介紹的方法不是真正的將屏幕裡的內容,確切的反射到幾何體正確位置上面,但 Sphere Mapping 是個快速製造出反射圖形的一種方法。

在了解本篇內容前,請先參考 Quadrics in OpenGL (L9) ,裡面介紹了 Quadric 的繪製方法。至於本篇主旨雖然是說球形映射,但其實也可以用在任何二次曲面上或方體上面。首先,要創造出如金屬般的反射效果,要先從 Photoshop 製造出一個球形曲面,即是利用 Photoshop 裡面的濾鏡 >> 扭曲 >> 魚眼效果,得到一張球形曲面影像,取代用魚眼鏡頭拍攝。(PS. 在 OpenGL 裡面的 texture image size 盡量都改成 2 的冪次方,這樣 rendering 效果會比較好。)

castle

castle_fisheye

[ 背景影像;球形扭曲影像。]

所以首先將上面兩張圖 load 到 texture[6] 陣列裡面,而每張影像有三種不同的 texture filter,code 如下。

void myopenGL::loadGLTexture()
{
	//@ texture 1
	string in_file = "D:\\4_OpenGL\\castle_fisheye.bmp";
	QImage img[2], GL_img[2];
	if(!img[0].load(in_file.c_str()))
	{
		qWarning("Could not read the image file.");
		QImage dummy(512, 512, QImage::Format_RGB32);
		dummy.fill(Qt::green);
		img[0] = dummy;
	}
	GL_img[0] = QGLWidget::convertToGLFormat( img[0] ); // convert QImage to GLformat
	//@ texture 2
	string in_file2 = "D:\\4_OpenGL\\castle.bmp";
	if(!img[1].load(in_file2.c_str()))
	{
		qWarning("Could not read the image file.");
		QImage dummy(512, 512, QImage::Format_RGB32);
		dummy.fill(Qt::green);
		img[1] = dummy;
	}
	GL_img[1] = QGLWidget::convertToGLFormat( img[1] ); // convert QImage to GLformat

	glGenTextures(6, &texture[0]);	// create three texture for two images

	for(int loop = 0; loop < 2; loop++)
	{
		//@ nearesr filtered texture
		glBindTexture(GL_TEXTURE_2D, texture[loop]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img[loop].width(), GL_img[loop].height(), 0,
					GL_RGBA, GL_UNSIGNED_BYTE, GL_img[loop].bits());
		//@ linear filtered texture
		glBindTexture(GL_TEXTURE_2D, texture[loop+2]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img[loop].width(), GL_img[loop].height(), 0,
					GL_RGBA, GL_UNSIGNED_BYTE, GL_img[loop].bits());
		//@ MipMapped texture
		glBindTexture(GL_TEXTURE_2D, texture[loop+4]);
		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[loop].width(), GL_img[loop].height(),
						GL_RGBA, GL_UNSIGNED_BYTE, GL_img[loop].bits());
	}

}

接著,這裡是本篇 sphere mapping 的重點,我們透過 glTexGeni 來產生紋理,而紋理座標(S, T, R, Q)相應著物體座標(x, y, z, w),所以如果今天用的是一維紋理( 1D ) 則只需要定義 S 座標,而 2D texture 則是定義 S 和 T 座標。

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP) 透過 glTexGeni 來定義 S 軸(水平軸) 的紋理特性。

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP) 則是定義 T 軸(垂直軸)的紋理特性。

在 glTexGeni 裡第三個參數是用來決定紋理的行為模式,除了 GL_SPHERE_MAP 外,還有 GL_OBJECT_LINEARGL_EYE_LINEARGL_SPHERE_MAPGL_REFLECTION_MAPGL_NORMAL_MAP

GL_OBJECT_LINEAR : 物體模式,紋理會隨著物體轉動而轉動。

GL_EYE_LINEAR : 視覺模式,紋理不會隨著物體轉動,始終保持原樣。

GL_SPHERE_MAP : 環境紋理(球體貼圖),具有反射效果。

GL_REFLECTION_MAP : 環境紋理(反射紋理),也具有反射效果。

GL_NORMAL_MAP : 用於立方體貼圖。

所以要在 initializeGL() 裡面定義紋理的行為模式。

	//@ sphere mapping
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

最後,在 painGL() 裡面將 GL_TEXTURE_GEN_SGL_TEXTURE_GEN_T 啟動後,即可看到 sphere mapping 的效果了,painGL() 裡面我透過 quadratic_object 來變化不同的二次曲面,這部分在 lesson 9 有詳細的說明了,這裡就不再敘述。後面就是把背景圖也給畫出來而已。

void myopenGL::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();		
	glTranslatef(0.0f, 0.0f, -10.0f);	
	//@ sphere mapping
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glBindTexture(GL_TEXTURE_2D, texture[4]);
	glPushMatrix();
	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);

	if(quadratic_object > 3)
		quadratic_object = 0;

	switch(quadratic_object)
	{
	case 0:
		drawCube();
		break;
	case 1:
		glTranslatef(0.0f, 0.0f, -1.5f);
		gluCylinder(quadratic, 1.0f, 1.0f, 3.0f, 32, 32);
		break;
	case 2:
		gluSphere(quadratic, 1.3f, 32, 32);
		break;
	case 3:
		glTranslatef(0.0f, 0.0f, -1.5f);
		gluCylinder(quadratic, 1.0f, 0.0f, 3.0f, 32, 32);
		break;
	}
	glPopMatrix();
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	//@ print the background
	glBindTexture(GL_TEXTURE_2D, texture[5]);
	glPushMatrix();
	glTranslatef(0.0f, 0.0f, -24.0f);
	glBegin(GL_QUADS);
	glNormal3f(0.0f, 0.0f, 1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-13.3f, -10.0f, 10.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 13.3f, -10.0f, 10.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 13.3f,  10.0f, 10.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-13.3f,  10.0f, 10.0f);
	glEnd();

	glPopMatrix();
}

 

執行結果:

2016-03-10_135153[球型反射曲面。]

2016-03-10_135249[錐形反射曲面。]

 

 


reference : 

http://nehe.gamedev.net/tutorial/sphere_mapping_quadrics_in_opengl/15005/

http://tiankefeng0520.iteye.com/blog/2007939

 

Leave a Reply

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