《计算机图形学》这门课的第二个实验,觉得还挺有意思的…
实验要求
设计一个OpenGL程序,创建一个三维迷宫,支持替身通过一定交互手段在迷宫中漫游,基本功能包括:
- 迷宫应当至少包含10 * 10 个Cell,不能过于简单,下图给出一种示例。
- 读取给定的替身模型,加载到场景中。
- 键盘方向键控制替身转向与漫游。
- 有碰撞检测,替身不应当穿墙。
- 支持切换第一视角和第三视角进行观察。
- 迷宫场景中的墙、地面等应贴上纹理。
附加要求(可选择一个):
- 同时加入二维辅助地图,替身在三维迷宫探索的同时,在小地图中显示已经探索的区域;(我选的是这一个,其他的由于最近事情比较多就没有做,以后有机会再补吧…如果有机会的话…=。=)
- 在俯视状态下,可以通过鼠标点选替身需要到达的目的地,通过寻径算法,控制替身自动到达目的地;
- 迷宫地图交互编辑功能,例如,可以设计一个二维地图编辑器,根据用户的绘制,拉伸得到三维迷宫场景;
- 其他相当难度,可以增加迷宫游戏趣味性的功能(需要通过指导老师认可)
实现过程
投影模式采用透视投影,第一、三视角之间的切换只需改变gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
的参数值即可。
main()
方法:
int main(int argc,char **argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowPosition(300, 50);
glutInitWindowSize(700, 700);
glutCreateWindow("迷宫");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
init()
方法:
void init(){
glClearColor(0.8, 0.8, 0.8, 1);//用灰色清屏
glEnable(GL_DEPTH_TEST);//开启深度测试
glEnable(GL_TEXTURE_2D);//启用二维纹理
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50, 1, 0.5, 100);/*其实一开始ZNear设的是1~*/
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(lookX, lookY, lookZ, centerX,centerY,centerZ,upX,upY,upZ);
glShadeModel(GL_SMOOTH); //使用平滑明暗处理
glEnable(GL_DEPTH_TEST); //剔除隐藏面
glEnable(GL_CULL_FACE); //不计算多边形背面
glFrontFace(GL_CCW); //多边形逆时针方向为正面
{
//三视图光源
GLfloat light0_position[] = {10.0f, 10.0f, 0.0f, 1.0f };
GLfloat light0_ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f };
GLfloat light0_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
//一视图光源
GLfloat light1_position[] = { 10.0f, 10.0f, 0.0f, 1.0f };
GLfloat light1_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat light1_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat light1_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
glEnable(GL_LIGHTING);//开启光照明
glEnable(GL_DEPTH_TEST);
}
参数的初始值:
double lookX=0, lookY=0, lookZ=15;//相机在世界座标的位置
double centerX = 0, centerY = 0, centerZ = 0;//相机镜头对准的物体在世界座标的位置
double upX = 0, upY = 1, upZ = 0;//相机向上的方向在世界座标中的方向
关于gluPerspective()
的理解推荐看OpenGL函数思考-gluPerspective。gluLookAt()
挺好理解的就不推荐了。
display()
方法:
void display(){
drawMaze();//画迷宫
glutSwapBuffers();/*这句很重要!!!刚开始学OpenGL的时候没注意这个,结果画出来什么都没有是全白的。开了双缓冲就要写。*/
}
迷宫我是用对应位置及数量的小立方体拼起来的,所以先声明了一个方法drawCube(double x, double y, double z)
专门用于绘制立方体。
void drawCube(double x, double y, double z){
glPushMatrix();/*!划重点!不写后果很严重(当然这个错误不是我犯得哈哈)*/
glTranslatef(x, y, z);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glNormal3f(0.0f, 1.0f, 0.0f);//法向量,光照明要用的
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, -1.0f);
glVertex3f(0.5f, 0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
glEnd();
glPopMatrix();/*有Push当然要有Pop*/
}
画迷宫我是用数组存的,因为还要实现同时加入二维辅助地图,替身在三维迷宫探索的同时,在小地图中显示已经探索的区域这个功能。(感觉很弱鸡啊…但是也没想出其他好办法…好在跑起来不卡…哈…哈…)
声明数组int maze[900]
用于存储一个30x30的迷宫。用0和1初始化数组。
- maze[i]=0: 座标(i - i / 30 * 30, i / 30,-20)处不绘制立方体。
- maze[j]=1: 座标(j - j / 30 * 30, j / 30,-20)处绘制立方体。
(初始化数组的过程赶时间写的超烂…不放了就…)
void drawMaze(){
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 700, 700);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0,700,700);
glEnable(GL_LIGHT0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50, 1, 0.5, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(lookX, lookY, lookZ, centerX, centerY, centerZ, upX, upY, upZ);
//drawGamer();
//画三维地图
glPushMatrix();
glTranslatef(-15, -14, 0);
for (int i = 0; i < 900; i++){
if (maze[i] == 1||maze[i]==-1){
drawCube(i - i / 30 * 30, i / 30,-20);
}
}
glPopMatrix();
//画地板
glPushMatrix();
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(14, 15, -20.5);
glVertex3f(-15, 15, -20.5);
glVertex3f(-15, -14.9, -20.5);
glVertex3f(14, -14.9, -20.5);
glEnd();
glPopMatrix();
}
效果应该就是这样orz有点丑…贴上纹理会好很多的…
有空再接着写实现了全部功能的:)
完整源代码下载