OpenGL學習——glut/ 場景漫遊,對gluLookAt()函數的深入理解

這個程序屬於上一篇文章的擴展吧,星球旋轉的那個程序主要是去了解它是怎麼旋轉的,很多函數是第一次見,學完之後可能有點懵。而且因爲建模的問題,畫圓的位置,視角位置,相機位置,設計的都不是太直觀,不太好觀察,並不便於理解函數的各個參數對呈現效果的影響。

所以這篇文章的程序會建出更容易理解和觀察的模型:在x和z軸上繪製一個大四邊形作爲陸地,在陸地上繪製36個雪人,他們可以作爲參照物。用戶可以通過鍵盤調整相機(人眼位置),使其向前向後移動,向左向右移動,向上向下移動,以及繞着y軸旋轉相機角度。

注意理解每種變換,是如何更改函數參數的,理解這個程序之後,一定會對gluLookAt()等函數有更好的理解,對視角有更好的理解。

學習要點

通過相機的變換呈現效果,以及相對應的gluLookAt()參數的變化去理解這個函數。

繪製場景的時候,多次用到glPushMatrix()和glPopMatrix()。

要點簡單解析

矩形入棧出棧

對於矩形的入棧出棧,可以先這樣理解,OpenGL在矩形所在的位置繪圖,如果想要在其他位置繪圖,需要對矩形做一個變換。然而繪製完之後,再次繪圖是以當前這個矩形位置爲基礎的,如果想回到原位置還要對它進行方向操作。那麼問題來了,如果對矩形的變換過多的話,還原起來就非常麻煩。所以解決辦法就是:把原矩形入棧保護起來,當矩形變換完畢後,再出棧就可以恢復到原先狀態。

視線變換思路

gluLookAt()函數使用的要點在於兩個點的座標,一個人眼點座標,一個看的點座標,所以需要正確設置這兩個點的座標。

下面簡單講一下本程序是如何設置這兩個點座標的。

設置xyz作爲相機位置座標,設置三個偏移量lx,ly,lz作爲視線方向(偏移量,大小爲0,0,-1),相機位置座標加入視線方向,就是看的點的座標。

旋轉相機可能不太好理解,我畫個圖幫忙理解下吧。
在這裏插入圖片描述
關於雪人是如何繪製的,這個有關具體繪製的操作,先不細究了吧,反正到時候貼圖可能更多一點,不過要是有興趣可以看看代碼咋畫的。這個代碼還是蠻值得學學的哈,場景漫遊我們回頭也得做。最後有啥不太理解的一定要問哈。

#include <gl/glut.h>
#include <math.h>

static float angle = 0.0;//angle繞y軸的旋轉角
static float x = 0.0f, y = 1.75f, z = 5.0f;//相機位置
static float lx = 0.0f, ly = 0.0f, lz = -1.0f;//視線方向,初始設爲沿着Z軸負方向

//定義觀察方式
void changeSize(int w, int h)
{
	glMatrixMode(GL_PROJECTION);    //投影變換
	glLoadIdentity();
	//設置視口爲整個窗口大小
	glViewport(0, 0, w, h);
	//設置可視空間
	gluPerspective(45, 1.0f*w / h, 1, 1000); //角度45, 窗口縱橫比, 眼睛所及距離(近和遠)

	glMatrixMode(GL_MODELVIEW);     //模型變換
	glLoadIdentity();
	//相機(人眼)位置, 眼睛看的點(相機位置+視線方向), 觀察者本身方向(角度,比如正立)
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

//繪製雪人
void drawSnowMan()
{
	glColor3f(1.0f, 1.0f, 1.0f);    //白色

	//畫身體
	glTranslatef(0.0f, 0.75f, 0.0f);
	glutSolidSphere(0.75f, 20, 20); //畫圓

	//畫頭
	glTranslatef(0.0f,1.0f,0.0f);
	glutSolidSphere(0.25f,20,20);   //畫圓

	//畫眼睛
	glPushMatrix();
	glColor3f(0.0f,0.0f,0.0f);  //黑色
	glTranslatef(0.05f,0.10f,0.18f);
	glutSolidSphere(0.05f,10,10);   //畫圓
	glTranslatef(-0.1f,0.0f,0.0f);
	glutSolidSphere(0.05f,10,10);   //畫圓
	glPopMatrix();

	//畫鼻子
	glColor3f(1.0f,0.5f,0.5f);  //有點像粉色
	glRotatef(0.0f,1.0f,0.0f,0.0f);
	glutSolidCone(0.08f,0.5f,10,2); //畫圓錐  參數::半徑,高
}

//渲染場景,畫地面,畫雪人
void renderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清空顏色和深度緩衝

	//畫地面
	glBegin(GL_QUADS);  //四邊形
	glVertex3f(-100.0f, 0.0f, -100.0f);
	glVertex3f(-100.0f, 0.0f, 100.0f);
	glVertex3f(100.0f, 0.0f, 100.0f);
	glVertex3f(100.0f, 0.0f, -100.0f);
	glEnd();

	//畫36個雪人
	for (int i= -3; i< 3; i++)
		for (int j = -3; j< 3; j++) {
			glPushMatrix(); //把當前矩形壓棧,這樣後面的操作不會影響到原矩形(標準位置/變換的矩形)
			//問題:那現在操作的是哪個矩形呢??壓棧保存了原矩形,但是當前的矩形還是標準(原)矩形,可以操作
			glTranslatef(i*10.0, 0, j*10.0);
			drawSnowMan();  //繪製雪人
			glPopMatrix();  //當把標準矩形移到了預期位置,再把原矩形彈棧恢復,給下次操作提供標準矩形
		}
	glutSwapBuffers();  //交換緩衝區
}

//旋轉相機,繞y軸旋轉
void orientMe(float ang)
{
	lx = sin(ang);
	lz = -cos(ang);
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
	//把所看的點(即視線方向上的點)理解爲在一個圓上旋轉,那設置的點的座標應該是旋轉的,通過圓半徑計算座標
	//!!!畫圖好理解!!!
}

//前後移動相機
void move_Front_Back(int direction)
{
	x = x + direction*(lx)*0.1;
	z = z + direction*(lz)*0.1;
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
    //同時移動相機和所看的點座標,只用修改x和z,視線參數不用修改,
}

//左右移動相機,一定要畫圖理解
void move_Left_Right(int direction)
{
    x = x + direction*(lz)*0.1;
	z = z - direction*(lx)*0.1;
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

void move_High_Low(int direction)
{
    y = y + direction * 0.1;
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

//鍵盤響應
void inputKey(unsigned char key, int x, int y) {
	switch (key)
	{
	case 'q':
		angle -= 0.03f;
		orientMe(angle);
		break;
	case 'e':               //q,e鍵調用相機旋轉
		angle += 0.03f;
		orientMe(angle);
		break;
    case 'w':
        move_Front_Back(1);
		break;
    case 's':
        move_Front_Back(-1);
		break;
    case 'a':
        move_Left_Right(1);
        break;
    case 'd':
        move_Left_Right(-1);
        break;              //wasd調整相機前後左右移動
    case '1':
        move_High_Low(1);
        break;
    case '2':
        move_High_Low(-1);
        break;              //1,2鍵上下移動相機
	default:
		break;
	}
}

int main(int argc,char **argv)
{
    //初始化,建立窗口
	glutInit(&argc, argv);
	//深度緩衝,雙緩衝,顏色緩衝
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(640, 360);
	glutCreateWindow("snowman test");

	glEnable(GL_DEPTH_TEST);        //開啓深度緩衝區
	glutKeyboardFunc(inputKey);     //鍵盤響應事件
	glutDisplayFunc(renderScene);   //繪製回調函數
	glutIdleFunc(renderScene);      //閒置時回調函數
	glutReshapeFunc(changeSize);    //調整窗口大小回調函數
	glutMainLoop();
    return 0;
}
//有待深入學習,理解
//glClear()的清楚深度緩衝區啥意思
//具體的繪圖細節,需要搭建模型,具體設計
//關於矩形的彈出彈入
//changeSize的用法,窗口回調函數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章