opengl学习笔记④——绕啊绕的行星系统(旋转,光照)
月亮环绕地球运转,在轨道上完成公转;而地球则环绕太阳运转,在轨道上完成公转。多层旋转关系在opengl中是一个复杂的技术细节?深入解析这一技术难点。
在OpenGL中进行的各种操作都会生成一个矩阵,在将其与表示顶点的向量相乘后得到最终的顶点信息。值得注意的是当我们在代码中按顺序输入操作1、2时对应的矩阵分别为A、B那么对于一个列向量V经过上述两个变换后的结果将是AB乘以V即ABV可以看出这种情况下执行顺序会对结果产生重要影响具体原因在于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);//月亮
我们以对顶点的操作为基础进行分析,并从上至下地探讨问题。让我们分析一下月球经历了哪些变化。
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;
}
