在不少游戏中,我们都可能会出现这样的场景,我们在一个建筑内行走,这个建筑有房间也有走廊,这时候在漫游中就有这样一个要求:人不能穿墙。当人物不被绘制而是以人物视野来代替时,这个问题就抽象为漫游时视点与场景碰撞检测的问题,可以看出它和标准的碰撞检测是有区别的,一般的碰撞检测问题考虑的是两个包围盒是否存在交,而在这里我们把可以把问题变得更容易,判断视线与包围盒是否存在交,如果存在,进行怎样的修补保证不穿墙,因为我们可以发现,当视线和包围盒产生交点的时候,人就已经或者即将穿墙。同样的思想在光线追踪算法中也是适用的,我们也是给物体先做一个包围盒,然后再与光线求交。
而作为编程练习而言,我把这个问题又更进一步的简化了:使用以轴向平行的线段为边的AABB包围盒,忽略y轴信息(因为我们不需要检测是否会穿过天花板和地板),转换为二维的碰撞检测。这个时候,我们事实上判断线线是否相交即可,也就是视线和包围盒正方形的四个边分别判断是否相交。在算法导论中已经给出了判断线段相交的快速算法,在这里我们可以直接使用。
我们知道求交是一个比较麻烦的运算,当然在这里我们做的并不是求交,而是判断是否存在交点,对于计算机而言,判断是与否相比起说出是什么,要容易得多。尽管如此,这个算法依然非常糟糕,在这里我考虑到了8种一般情况和4种特殊情况,但是这些情况的判定不是完全独立的,它们之间有很小的交集,但正是这样的交集,导致情况的错误识别,整个算法在某些比较特殊的方位会穿墙,或者会出现视角跳变。这些问题都是可以通过多进行分类讨论解决,在这里暂时没有做出修正。
同样,在性能上,我们还可以做很多优化,首先,我们发现碰撞检测是在人物移动的每个按键响应事件中执行的,所有的物体无论和视点隔得有多远,我们都会进行碰撞检测的计算,这其中的计算量不能算少,更为聪明的做法应当是首先进行一个粗略的排查,把不太可能发生碰撞的物体剔除掉,后续不进行相关运算,在这里我们需要借助一些用于场景管理的数据结构,如八叉树,四叉树等等,思想是递归的二分(或更多)划分空间,最终确定物体所在空间。这里只是一个改进的方案,并没有具体实现。
当然,在这里,包围盒有内外两种,一种是我们走不出,一种是我们走不进,对于一个房间而言,那么我们应该是走不出(不考虑门),对于一个物体而言,我们应该是走不进,在这里走不出的碰撞检测实现起来比较容易,因为它的四个面不会产生干扰,不过在这里我把它写的更简单了,不考虑人物当前方向直接纠正方向为轴向,所以当人物斜撞墙时会自动纠正为贴墙走。而走不进的碰撞检测则留意改进了这一点。
以下讨论第二种碰撞检测:
8种一般情况:
//视线与A面线段相交
1.前进碰撞A面
2.后退碰撞A面
//视线与B面线段相交
3.前进碰撞B面
4.后退碰撞B面
//视线与C面线段相交
5.前进碰撞C面
6.后退碰撞C面
//视线与D面线段相交
7.前进碰撞D面
8.后退碰撞D面
4种特殊情况:
不与任何线相交,平行进入物体内部(因为存在浮点误差,这里的平行也是相对的概念)
1.平行于z轴进入A面
2.平行于z轴进入D面
3.平行于x轴进入C面
4.平行于x轴进入B面
为什么会出现这四种特殊情况?因为我们可能在平行的过程中左右移动,这时我们会闯入物体,但是由于移动是离散的,所以我们的视线不一定与物体相交!
那么,显然易见,当碰撞发生后,我们要做的就是“恢复现场”,也就是说,当视线与边缘相交时,我们把它恢复到恰好不相交的位置,如下:
在这个过程中,我们维护角度不发生变化。
注:操作方法 - WSAD控制上下左右移动 IKJL控制视角上下左右转变,随便找3张贴图 代码即可运行。
test.h
#pragma once
#define GLUT_DISABLE_ATEXIT_HACK
#include "GL/GLUT.H"
void drawRect(GLuint texture);
void drawCube(GLuint texture);
void loadTex(int i, char *filename, GLuint* texture);
typedef enum { left, right, front, back }direction;
void move(float* eye, float* center, direction dir);
void rotate(float* eye, float* center, direction dir);
void inCollisionTest(float* eye, float* center, float x1, float z1,
float x2, float z2);
void outCollisionTest(float* eye, float* center, float x1, float z1,
float x2, float z2);
texture.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
#include"test.h"
#define BITMAP_ID 0x4D42
//读纹理图片
static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
FILE *filePtr; // 文件指针
BITMAPFILEHEADER bitmapFileHeader; // bitmap文件头
unsigned char *bitmapImage; // bitmap图像数据
int imageIdx = 0; // 图像位置索引
unsigned char tempRGB; // 交换变量
// 以“二进制+读”模式打开文件filename
filePtr = fopen(filename, "rb");
if (filePtr == NULL) {
printf("file not open\n");
return NULL;
}
// 读入bitmap文件图
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
// 验证是否为bitmap文件
if (bitmapFileHeader.bfType != BITMAP_ID) {
fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");
return NULL;
}
// 读入bitmap信息头
fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
// 将文件指针移至bitmap数据
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
// 为装载图像数据创建足够的内存
bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];
// 验证内存是否创建成功
if (!bitmapImage) {
fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
return NULL;
}
// 读入bitmap图像数据
fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
// 确认读入成功
if (bitmapImage == NULL) {
fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
return NULL;
}
//由于bitmap中保存的格式是BGR,下面交换R和B的值,得到RGB格式
for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}
// 关闭bitmap图像文件
fclose(filePtr);
return bitmapImage;
}
//加载纹理的函数
void loadTex(int i, char *filename, GLuint* texture)
{
BITMAPINFOHEADER bitmapInfoHeader; // bitmap信息头
unsigned char* bitmapData; // 纹理数据
bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader);
glBindTexture(GL_TEXTURE_2D, texture[i]);
// 指定当前纹理的放大/缩小过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,
0, //mipmap层次(通常为,表示最上层)
GL_RGB, //我们希望该纹理有红、绿、蓝数据
bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2
bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2
0, //边框(0=无边框, 1=有边框)
GL_RGB, //bitmap数据的格式
GL_UNSIGNED_BYTE, //每个颜色数据的类型
bitmapData); //bitmap数据指针
}
test.cpp
#include<stdio.h>
#include<math.h>
#include"test.h"
const float PI = 3.1415926536f;
float min(float x, float y)
{
return x < y ? x : y;
}
float max(float x, float y)
{
return x > y ? x : y;
}
struct dot {
float x;
float y;
dot(float _x, float _y) :x(_x), y(_y) { }
};
double Direction(dot pi, dot pj, dot pk) {
return (pk.x - pi.x)*(pj.y - pi.y) - (pj.x - pi.x)*(pk.y - pi.y);
}
bool OnSegment(dot pi, dot pj, dot pk) {
if ((min(pi.x, pj.x) <= pk.x) && (pk.x <= max(pi.x, pj.x))
&& (min(pi.y, pj.y) <= pk.y) && (pk.y <= max(pi.y, pj.y)))
return true;
else return false;
}
bool SegmentIntersect(dot p1, dot p2, dot p3, dot p4)
{
int d1, d2, d3, d4;
d1 = Direction(p3, p4, p1);
d2 = Direction(p3, p4, p2);
d3 = Direction(p1, p2, p3);
d4 = Direction(p1, p2, p4);
if (((d1>0 && d2<0) || (d1<0 && d2>0)) && ((d3>0 && d4<0) || (d3<0 && d4>0)))
return true;
else if (d1 == 0 && OnSegment(p3, p4, p1))
return true;
else if (d2 == 0 && OnSegment(p3, p4, p2))
return true;
else if (d3 == 0 && OnSegment(p1, p2, p3))
return true;
else if (d4 == 0 && OnSegment(p1, p2, p4))
return true;
else return false;
}
float abs(float x)
{
return x >= 0 ? x : -x;
}
void move(float* eye, float* center, direction dir)
{
const float d = 1.0f;
float x1, x2, y1, y2, x, y;
x1 = eye[0], y1 = eye[2], x2 = center[0], y2 = center[2];
float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
if (x2 - x1<0.1f&&x2 - x1>-0.1f)x2 = x1;
if (y2 - y1 < 0.1f&&y2 - y1>-0.1f)y2 = y1;
switch (dir) {
case front: {
eye[0] = d*(x2 - x1) / len + x1;
eye[2] = d*(y2 - y1) / len + y1;
center[0] = eye[0] + x2 - x1;
center[2] = eye[2] + y2 - y1;
break;
}
case back: {
eye[0] = d*(x1 - x2) / len + x1;
eye[2] = d*(y1 - y2) / len + y1;
center[0] = eye[0] + x2 - x1;
center[2] = eye[2] + y2 - y1;
break;
}
case left: {
eye[0] = d*(y2 - y1) / len + x1;
eye[2] = d*(x1 - x2) / len + y1;
center[0] = eye[0] + x2 - x1;
center[2] = eye[2] + y2 - y1;
break;
}
case right: {
eye[0] = d*(y1 - y2) / len + x1;
eye[2] = d*(x2 - x1) / len + y1;
center[0] = eye[0] + x2 - x1;
center[2] = eye[2] + y2 - y1;
break;
}
default:break;
}
return;
}
void rotate(float* eye, float* center, direction dir)
{
const float alpha = 1.0f / (10 * PI);
float x1, x2, y1, y2;
x1 = eye[0], y1 = eye[2], x2 = center[0], y2 = center[2];
switch (dir) {
case left: {
center[0] = x1 + (x2 - x1)*cos(alpha) + (y2 - y1)*sin(alpha);
center[2] = y1 + (y2 - y1)*cos(alpha) - (x2 - x1)*sin(alpha);
break;
}
case right: {
center[0] = x1 + (x2 - x1)*cos(alpha) - (y2 - y1)*sin(alpha);
center[2] = y1 + (y2 - y1)*cos(alpha) + (x2 - x1)*sin(alpha);
break;
}
default:break;
}
}
void outCollisionTest(float* eye, float* center, float x1,float x2,float z1, float z2)
{
if (x1 < 0)x1 += 2;
else x1 -= 2;
if (x2 < 0)x2 += 2;
else x2 -= 2;
if (z1 < 0)z1 += 2;
else z1 -= 2;
if (z2 < 0)z2 += 2;
else z2 -= 2;
if (center[0] < x1) {
center[0] = x1;
}
if (center[0] > x2) {
center[0] = x2;
}
if (center[2] < z1) {
center[2] = z1;
}
if (center[2] > z2) {
center[2] = z2;
}
float distance = sqrt((eye[0] - center[0])*(eye[0] - center[0]) +
(eye[2] - center[2])*(eye[2] - center[2]));
if (distance <= 2.0f) {
eye[0] = 2.0f*(eye[0] - center[0]) / distance + center[0];
eye[2] = 2.0f*(eye[2] - center[2]) / distance + center[2];
}
bool flag = false;
if (eye[0] < x1) {
flag = true;
eye[0] = x1;
}
if (eye[0] > x2) {
flag = true;
eye[0] = x2;
}
if (eye[2] < z1) {
flag = true;
eye[2] = z1;
}
if (eye[2] > z2) {
flag = true;
eye[2] = z2;
}
if (flag) {
distance = sqrt((eye[0] - center[0])*(eye[0] - center[0]) +
(eye[2] - center[2])*(eye[2] - center[2]));
if (distance <=2.0f) {
center[0] = 2.0f*(center[0] - eye[0]) / distance + eye[0];
center[2] = 2.0f*(center[2] - eye[2]) / distance + eye[2];
}
}
return;
}
void inCollisionTest(float* eye, float* center, float x1, float z1,
float x2, float z2)
{
//printf("%f,%f,%f,%f\n", center[0], center[2], eye[0], eye[2]);
const float d = 2.0f;
float _x1 = center[0], _x2 = eye[0], _z1 = center[2], _z2 = eye[2];
float len = sqrt((_x2 - _x1)*(_x2 - _x1) + (_z2 - _z1)*(_z2 - _z1));
dot d1(eye[0], eye[2]),d2(center[0],center[2]);
dot d3(x1, z1), d4(x1, z2), d5(x2, z1), d6(x2, z2);
if (SegmentIntersect(d1,d2,d4,d6)) {
if (center[2] < eye[2]) {
printf("1\n");
center[0] = _x1 + (_x2 - _x1)*(z2 - _z1) / (_z2 - _z1);
center[2] = z2;
eye[0] = center[0] + d*(_x2 - _x1) / len;
eye[2] = center[2] + d*(_z2 - _z1) / len;
}
else if (center[2] > eye[2]) {
printf("2\n");
eye[0] = _x2 + (_x1 - _x2)*(z2 - _z2) / (_z1 - _z2);
eye[2] = z2;
center[0] = eye[0] + d*(_x1 - _x2) / len;
center[2] = eye[2] + d*(_z1 - _z2) / len;
}
}
else if (SegmentIntersect(d1, d2, d5, d6)) {
if (center[0]<eye[0]) {
printf("3\n");
center[0] = x2;
center[2] = _z1 + (_z2 - _z1)*(x2 - _x1) / (_x2 - _x1);
eye[0] = center[0] + d*(_x2 - _x1) / len;
eye[2] = center[2] + d*(_z2 - _z1) / len;
}
else if (center[0]>eye[0]) {
printf("4\n");
eye[0] = x2;
eye[2] = _z2 + (_z1 - _z2)*(x2 - _x2) / (_x1 - _x2);
center[0] = eye[0] + d*(_x1 - _x2) / len;
center[2] = eye[2] + d*(_z1 - _z2) / len;
}
}
else if (SegmentIntersect(d1, d2, d3, d5)) {
if (center[2] > eye[2]) {
printf("5\n");
center[0] = _x1 + (_x2 - _x1)*(z1 - _z1) / (_z2 - _z1);
center[2] = z1;
eye[0] = center[0] + d*(_x2 - _x1) / len;
eye[2] = center[2] + d*(_z2 - _z1) / len;
}
else if (center[2] < eye[2]) {
printf("6\n");
eye[0] = _x2 + (_x1 - _x2)*(z1 - _z2) / (_z1 - _z2);
eye[2] = z1;
center[0] = eye[0] + d*(_x1 - _x2) / len;
center[2] = eye[2] + d*(_z1 - _z2) / len;
}
}
else if (SegmentIntersect(d1, d2, d3, d4)) {
if (center[0] > eye[0]) {
printf("7\n");
center[0] = x1;
center[2] = _z1 + (_z2 - _z1)*(x1 - _x1) / (_x2 - _x1);
eye[0] = center[0] + d*(_x2 - _x1) / len;
eye[2] = center[2] + d*(_z2 - _z1) / len;
}
else if (center[0] < eye[0]) {
printf("8\n");
eye[0] = x1;
eye[2] = _z2 + (_z1 - _z2)*(x1 - _x2) / (_x1 - _x2);
center[0] = eye[0] + d*(_x1 - _x2) / len;
center[2] = eye[2] + d*(_z1 - _z2) / len;
}
}
else if (_x1 - _x2 < 0.1f&&_x2 - _x1 >= -0.1f
&& ((_z1>z1&&_z1<z2) || (_z2>z1&&_z2<z2))) {
if (_x1 > x1&&_x1 < (x1 + x2) / 2) {
center[0] = x1;
eye[0] = x1;
}
else if (_x1 >(x1 + x2) / 2 && _x1 < x2) {
center[0] = x2;
eye[0] = x2;
}
}
else if (_z1 - _z2 < 0.1f&&_z2 - _z1 >= -0.1f
&& ((_x1>x1&&_x1<x2) || (_x2>x1&&_x2<x2))) {
if (_z1 > z1&&_z1 < (z1 + z2) / 2) {
center[2] = z1;
eye[2] = z1;
}
else if (_z1 >(z1 + z2) / 2 && _z1 < z2) {
center[2] = z2;
eye[2] = z2;
}
}
}
void drawRect(GLuint texture)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture); //选择纹理texture[status]
const GLfloat x1 = -0.5, x2 = 0.5;
const GLfloat y1 = -0.5, y2 = 0.5;
const GLfloat point[4][2] = { { x1,y1 },{ x1,y2 },{ x2,y2 },{ x2,y1 } };
int dir[4][2] = { { 1,1 },{ 1,0 },{ 0,0 },{ 0,1 } };
glBegin(GL_QUADS);
for (int i = 0; i < 4; i++) {
glTexCoord2iv(dir[i]);
glVertex2fv(point[i]);
}
glEnd();
glDisable(GL_TEXTURE_2D);
}
void drawCube(GLuint texture)
{
glEnable(GL_TEXTURE_2D);
int i, j;
const GLfloat x1 = -0.5, x2 = 0.5;
const GLfloat y1 = -0.5, y2 = 0.5;
const GLfloat z1 = -0.5, z2 = 0.5;
//指定六个面的四个顶点,每个顶点用3个坐标值表示
//前 后 上 下 左 右
GLfloat point[6][4][3] = {
{ { x1,y1,z1 },{ x1,y2,z1 },{ x2,y2,z1 },{ x2,y1,z1 } },
{ { x1,y1,z2 },{ x2,y1,z2 },{ x2,y2,z2 },{ x1,y2,z2 } },
{ { x1,y2,z1 },{ x1,y2,z2 },{ x2,y2,z2 },{ x2,y2,z1 } },
{ { x1,y1,z1 },{ x2,y1,z1 },{ x2,y1,z2 },{ x1,y1,z2 } },
{ { x2,y1,z1 },{ x2,y2,z1 },{ x2,y2,z2 },{ x2,y1,z2 } },
{ { x1,y1,z1 },{ x1,y1,z2 },{ x1,y2,z2 },{ x1,y2,z1 } },
};
int dir[6][4][2] = {
{ { 0,0 },{ 0,1 },{ 1,1 },{ 1,0 } },
{ { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } },
{ { 0,1 },{ 0,0 },{ 1,0 },{ 1,1 } },
{ { 1,1 },{ 0,1 },{ 0,0 },{ 1,0 } },
{ { 1,0 },{ 1,1 },{ 0,1 },{ 0,0 } },
{ { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } },
};
for (i = 0; i < 6; i++) {
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
for (j = 0; j < 4; j++) {
glTexCoord2iv(dir[i][j]);
glVertex3fv(point[i][j]);
}
glEnd();
}
glDisable(GL_TEXTURE_2D);
}
main.cpp
// glutEx1.cpp : 定义控制台应用程序的入口点。
//
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include"test.h"
#include<io.h>
#include <math.h> /* for cos(), sin(), and sqrt() */
#define roomSizeX 100
#define roomSizeY 15
#define roomSizeZ 100
GLuint texture[3];
//视区
float whRatio;
int wHeight = 0;
int wWidth = 0;
//视点
float center[] = { 0, 0, 35 };
float eye[] = { 0, 0, 40 };
void drawScene()
{
//地板
glPushMatrix();
glTranslatef(0.0f, -1.0f*roomSizeY / 2.0f, 0.0f);
glRotatef(90, 1, 0, 0);
glScalef(roomSizeX, roomSizeZ, 1);
drawRect(texture[0]);
glPopMatrix();
//天花板
glPushMatrix();
glTranslatef(0.0f, 1.0f*roomSizeY / 2.0f, 0.0f);
glRotatef(270, 1, 0, 0);
glScalef(roomSizeX, roomSizeZ, 1);
drawRect(texture[0]);
glPopMatrix();
//墙壁(前)
glPushMatrix();
glTranslatef(0.0f, 0.0f, -1.0f*roomSizeZ / 2.0);
glRotatef(180, 1, 0, 0);
glRotatef(180, 0, 0, 1);
glScalef(roomSizeX, roomSizeY, 1);
drawRect(texture[1]);
glPopMatrix();
//墙壁(后)
glPushMatrix();
glTranslatef(0.0f, 0.0f, 1.0f*roomSizeZ / 2.0f);
glScalef(roomSizeX, roomSizeY, 1);
drawRect(texture[1]);
glPopMatrix();
//墙壁(左)
glPushMatrix();
glTranslatef(-1.0f*roomSizeX / 2.0f, 0.0f, 0.0f);
glRotatef(270, 0, 1, 0);
glScalef(roomSizeZ, roomSizeY, 1);
drawRect(texture[1]);
glPopMatrix();
//墙壁(右)
glPushMatrix();
glTranslatef(1.0f*roomSizeX / 2.0f, 0.0f, 0.0f);
glRotatef(90, 0, 1, 0);
glScalef(roomSizeZ, roomSizeY, 1);
drawRect(texture[1]);
glPopMatrix();
//中间墙壁
glPushMatrix();
glScalef(50, 15, 50);
drawCube(texture[1]);
glPopMatrix();
//箱子
glPushMatrix();
glTranslatef(-1.0f*roomSizeX / 2.0f+2.5f, -1.0f*roomSizeY / 2.0f + 2.5f, -1.0f*roomSizeZ / 2.0f + 5.0f);
glScalef(5, 5, 5);
drawCube(texture[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(-1.0f*roomSizeX / 2.0f + 2.5f, -1.0f*roomSizeY / 2.0f + 7.5f, -1.0f*roomSizeZ / 2.0f + 5.0f);
glScalef(5, 5, 5);
drawCube(texture[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(-1.0f*roomSizeX / 2.0f + 7.5f, -1.0f*roomSizeY / 2.0f + 2.5f, -1.0f*roomSizeZ / 2.0f + 5.0f);
glScalef(5, 5, 5);
drawCube(texture[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(-1.0f*roomSizeX / 2.0f + 2.5f, -1.0f*roomSizeY / 2.0f +2.5f, -1.0f*roomSizeZ / 2.0f + 10.0f);
glScalef(5, 5, 5);
drawCube(texture[2]);
glPopMatrix();
}
void updateView(int height, int width)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影
glLoadIdentity(); //初始化矩阵为单位矩阵
whRatio = (GLfloat)width / (GLfloat)height; //设置显示比例
gluPerspective(45.0f, whRatio, 1.0f, 150.0f); //透视投影
//glFrustum(-3, 3, -3, 3, 3,100);
glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型
}
void reshape(int width, int height)
{
if (height == 0) //如果高度为0
{
height = 1; //让高度为1(避免出现分母为0的现象)
}
wHeight = height;
wWidth = width;
updateView(wHeight, wWidth); //更新视角
}
void idle()
{
glutPostRedisplay();
}
void init()
{
glEnable(GL_DEPTH_TEST);//开启深度测试
glEnable(GL_LIGHTING); //开启光照模式
glGenTextures(3, texture);
loadTex(0, "1.bmp", texture);
loadTex(1, "6.bmp", texture);
loadTex(2, "8.bmp", texture);
}
void key(unsigned char k, int x, int y)
{
switch (k)
{
case 27:
case 'q': {exit(0); break; } //退出
case 'a': { //左移
move(eye, center, left);
break;
}
case 'd': { //右移
move(eye, center, right);
break;
}
case 'w': { //前移
move(eye, center, front);
break;
}
case 's': { //后移
move(eye, center, back);
break;
}
case 'j': {//视角左移
rotate(eye, center, left);
break;
}
case 'l': {//视角右移
rotate(eye, center, right);
break;
}
case 'i': {//视角上移
center[1] += 0.4f;
break;
}
case 'k': {//视角上移
center[1] -= 0.4f;
break;
}
}
//与墙壁的碰撞检测
outCollisionTest(eye, center, -roomSizeX/2, roomSizeX / 2,-roomSizeZ/2, roomSizeZ / 2);
inCollisionTest(eye, center, -30, -30, 30, 30);
inCollisionTest(eye, center, -50, -45, -42, -37);
inCollisionTest(eye, center, -45, -50, -37, -42);
updateView(wHeight, wWidth); //更新视角
}
void redraw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); //初始化矩阵为单位矩阵
gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0); // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上
glPolygonMode(GL_FRONT, GL_FILL);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
// 启用光照计算
glEnable(GL_LIGHTING);
// 指定环境光强度(RGBA)
GLfloat ambientLight[] = { 2.0f, 2.0f, 2.0f, 1.0f };
// 设置光照模型,将ambientLight所指定的RGBA强度值应用到环境光
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
// 启用颜色追踪
glEnable(GL_COLOR_MATERIAL);
// 设置多边形正面的环境光和散射光材料属性,追踪glColor
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
drawScene();//绘制场景
glutSwapBuffers();//交换缓冲区
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);//对glut的初始化
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
//初始化显示模式:RGB颜色模型,深度测试,双缓冲
glutInitWindowSize(600, 600);//设置窗口大小
int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题
glutDisplayFunc(redraw); //注册绘制回调函数
glutReshapeFunc(reshape); //注册重绘回调函数
glutKeyboardFunc(key); //注册按键回调函数
glutIdleFunc(idle);//注册全局回调函数:空闲时调用
init();
glutMainLoop(); // glut事件处理循环
return 0;
}