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;
}


猜你喜欢

转载自blog.csdn.net/qq_38782152/article/details/80258560