1. 程式人生 > >opengl學習筆記④——繞啊繞的行星系統(旋轉,光照)

opengl學習筆記④——繞啊繞的行星系統(旋轉,光照)

月亮繞著地球轉,地球繞著太陽轉。多層旋轉關係在opengl裡是個啥玩意?讓我們來看一看。

在opengl中,我們的各種操作會最終產生一個矩陣,矩陣與代表頂點的向量相乘得到最終的頂點資訊。不過有趣的是,如果我們依次寫下操作1,2,對應矩陣A,B,如果頂點的列向量是V,則變換的結果V'=ABV。可以看到的是,後定義的B操作先應用在了V上。這是因為opengl內的矩陣是右乘的(具體問度娘)。

我不是很喜歡這種反著看的方法。所以我推薦把這些操作看成對座標系的操作,那就正常了。對於上面的例子,就可以認為是先把座標系做A操作再做B操作(因為原點和繪製物體的頂點是對立的關係,很有意思)。有點抽象,我們上具體例子。

要繪製一個簡單的日地月模型。我們有了一個基本的思路,先在原點畫太陽,再把座標系旋轉一定角度,再沿著某個方向平移,畫個地球,再旋轉座標系,然後平移,繪製月亮。程式碼如下:

	glutSolidSphere(50, 100, 100);//太陽
	glRotatef(clock()/10, 0, 0, 1);
	glTranslated(300, 0, 0);
	glutSolidSphere(30, 100, 100);//地球
	glRotated(clock()/10, 0, 0, 1);
	glTranslated(-100, 0, 0);
	glutSolidSphere(10, 30, 30);//月亮

我們如果按照對頂點的操作來看,由上到下,平移操作產生的矩陣定義為T1,T2,旋轉的為R1,R2。我們來看看月亮經歷了什麼。

R1T1R2T2:沿X移動-100,旋轉,沿著X移動300,旋轉。還是能夠理解的,但是和平常的習慣不一樣,所以還是推薦看成對座標系的操作。

但是這樣弄出來的不過是三個圓在轉,所以我們在太陽處新增額外光照,但是要設定太陽變成發光物體的話,我們需要對太陽的材質的GL_EMISSION屬性做出修改

glMaterialfv(GL_FRONT, GL_EMISSION, sun);

sun是一個數組,代表發光物體的RBGA。

這樣太陽就看起來像是發光了,但是實際上我們設定在太陽的點光源對太陽發光沒任何貢獻。也不會出現太陽會過濾點光源的光,使它變成太陽紅。所以我們對點光源的顏色,地球,月亮的材質做出設定,並且再在相機位置繫結一個微弱光源,這樣我們看行星背部也不會太黑。這樣這個行星系統看起來有點樣子。於是更新上面的程式碼如下     

        glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glLightfv(GL_LIGHT1, GL_POSITION, l_position);
	gluLookAt(b, c, a,
		0, 0, 0,
		0, 1, 0);
	glLightfv(GL_LIGHT0, GL_POSITION, l_position);
	glMaterialfv(GL_FRONT, GL_COLOR_INDEXES, color_index);
	glMaterialfv(GL_FRONT, GL_EMISSION, sun);
	glutSolidSphere(50, 100, 100);
	glMaterialfv(GL_FRONT, GL_EMISSION, re_set);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, earth);
	glRotatef(clock() / 10, 0, 0, 1);
	glTranslated(300, 0, 0);
	glutSolidSphere(30, 100, 100);
	glRotated(clock() / 10, 0, 0, 1);
	glTranslated(-100, 0, 0);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, moon);
	glutSolidSphere(10, 30, 30);

收工。原始碼如下,我們還是繼續使用了上一次裡的一些使用者互動操作函式。

#include<iostream>
#include<fstream>
#include<GL/glew.h>//使用glew庫使用VBO
#include <GL/glut.h>
#include<cmath>
#include<ctime>
#include<vector>
#include<string>
#define BUFFER_OFFSET(bytes) ((GLubyte*)NULL+bytes)
#define T 100
#pragma comment(lib, "glew32.lib")//加個glew使用VBO,嘖
using namespace std;
GLfloat l_position[4] = { 0,0,0,1 };
GLfloat r_x1, r_y1, r_x2, r_y2;//滑鼠拖動引數
GLint tag = 2;//初始表示畫多邊形
GLfloat mov_x1, mov_y1, mov_x2, mov_y2;//平移變數
GLfloat trans_x, trans_y;
GLfloat a, b, c, d, e, f;
GLint flag;//0表示滑鼠左鍵按下,1表示中鍵按下
GLfloat sun[] = { 1,0.2,0.2,1 };
GLfloat sun_light[] = { 1,0.8,0.8,1 };
GLfloat re_set[] = { 0,0,0.0,0 };
GLfloat earth[] = { 0,0.6,1,1 };
GLfloat earth_[] = { 0,0,0.1,0 };
GLfloat color_index[] = { 1,1,1 };
GLfloat moon[] = { 0.8,0.8,0.8,1 };
GLfloat light_1[] = { 0.2,0.2,0.2,1 };
void init(void)
{
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GLUT_MULTISAMPLE);
	glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, light_1);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glMatrixMode(GL_PROJECTION);//設定投影矩陣
	glLoadIdentity();
	gluPerspective(60, 1, 0.1, 100000);
	glClearColor(0, 0, 0, 1);
	a = 500;
	b = 20.0;
	c = 50.0;
	d = e = f = 0;
	trans_x = trans_y = 0;
	r_x1 = r_y1 = r_x2 = r_y2 = 0;
	mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0;
}
void timerFunc(int value)
{
	glutPostRedisplay();
	glutTimerFunc(10, timerFunc, 1);
}
void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glLightfv(GL_LIGHT1, GL_POSITION, l_position);
	gluLookAt(b, c, a,
		0, 0, 0,
		0, 1, 0);
	glLightfv(GL_LIGHT0, GL_POSITION, l_position);
	glMaterialfv(GL_FRONT, GL_COLOR_INDEXES, color_index);
	glMaterialfv(GL_FRONT, GL_EMISSION, sun);
	glutSolidSphere(50, 100, 100);
	glMaterialfv(GL_FRONT, GL_EMISSION, re_set);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, earth);
	glRotatef(clock() / 10, 0, 0, 1);
	glTranslated(300, 0, 0);
	glutSolidSphere(30, 100, 100);
	glRotated(clock() / 10, 0, 0, 1);
	glTranslated(-100, 0, 0);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, moon);
	glutSolidSphere(10, 30, 30);
	glutSwapBuffers();
}
void s_input(int key, int x, int y)//方向鍵改變相機Z
{
	if (key == GLUT_KEY_DOWN)
		a += 10;
	if (key == GLUT_KEY_UP)
		a -= 10;
}
void input(unsigned char key, int x, int y)
{
	switch (key)//你還是可以通過鍵盤控制引數
	{
	case'w':c += 10; break;//相機x,y移動
	case's':c -= 10; break;
	case'a':b -= 10; break;
	case'd':b += 10; break;
	case'h':d -= 5; break;//三個旋轉度設定
	case'k':d += 5; break;
	case'u':e -= 5; break;
	case'j':e += 5; break;
	case'i':f += 5; break;
	case'y':f -= 5; break;
	default:
		break;
	}

}
void start(int button, int state, int x, int y)
{
	if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動起始點
	{
		flag = 0;
		r_x1 = r_x2 = x;
		r_y1 = r_y2 = y;

	}
	if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON)//記錄旋轉拖動起始點
	{
		flag = 1;
		mov_x1 = mov_x2 = x;
		mov_y1 = mov_y2 = y;

	}
	if (state == GLUT_UP && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動最終點並將旋轉量儲存進d,e
	{
		d += (r_x2 - r_x1) / 3;
		e -= (r_y2 - r_y1) / 3;
		r_x1 = r_x2 = r_y1 = r_y2 = 0;
	}
	if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON)//記錄平移最終點,並將平移量存入trans_x,trans_y
	{
		trans_x += mov_x2 - mov_x1;
		trans_y += mov_y1 - mov_y2;
		mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0;
	}
}
void end(int x, int y)
{
	if (!flag)
	{
		r_x2 = x;
		r_y2 = y;
	}
	if (flag == 1)
	{
		mov_x2 = x;
		mov_y2 = y;
	}
}
void menu(int value)
{
	tag = value;
}
int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500, 500);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	GLenum err = glewInit();//初始化glew
	if (GLEW_OK != err)
	{
		exit(0);
	}
	init();
	glutDisplayFunc(display);
	glutTimerFunc(10, timerFunc, 1);
	glutSpecialFunc(s_input);
	glutKeyboardFunc(input);
	glutMouseFunc(start);
	glutMotionFunc(end);
	glutMainLoop();
	return 0;
}