[OpenGL]基于AABB包围盒的漫游时视点与场景的碰撞检测

在不少游戏中,我们都可能会出现这样的场景,我们在一个建筑内行走,这个建筑有房间也有走廊,这时候在漫游中就有这样一个要求:人不能穿墙。当人物不被绘制而是以人物视野来代替时,这个问题就抽象为漫游时视点与场景碰撞检测的问题,可以看出它和标准的碰撞检测是有区别的,一般的碰撞检测问题考虑的是两个包围盒是否存在交,而在这里我们把可以把问题变得更容易,判断视线与包围盒是否存在交,如果存在,进行怎样的修补保证不穿墙,因为我们可以发现,当视线和包围盒产生交点的时候,人就已经或者即将穿墙。同样的思想在光线追踪算法中也是适用的,我们也是给物体先做一个包围盒,然后再与光线求交。

而作为编程练习而言,我把这个问题又更进一步的简化了:使用以轴向平行的线段为边的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面

其余内容明天将继续完成

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;
}
时间: 2024-10-25 23:02:08

[OpenGL]基于AABB包围盒的漫游时视点与场景的碰撞检测的相关文章

AABB包围盒、OBB包围盒、包围球的比較

1) AABB 包围盒: AABB 包围盒是与坐标轴对齐的包围盒, 简单性好, 紧密性较差(尤其对斜对角方向放置的瘦长形对象, 採用AABB, 将留下非常大的边角空隙, 导致大量不是必需的包围盒相交測试).当物体旋转之后需对AABB 进行相同的旋转并更新; 当物体变形之后仅仅需对变形了的基本几何元素相应的包围盒又一次计算; 然后能够自下向上由子结点的AABB 合成父结点的AABB, 最后进行包围盒树的更新. 2) OBB 包围盒: OBB 碰撞检測方法紧密性是较好的, 可以大大降低參与相交測试的

在Spring中基于JDBC进行数据访问时如何控制超时

超时分类 超时根据作用域可做如下层级划分: Transaction Timeout > Statement Timeout > JDBC Driver Socket Timeout Transaction Timeout指一组SQL操作执行时应在设定的时间内完成(提交或回滚),否则将引发超时.它的值应大于 N(语句数) * Statement Timeout Statement Timeout指完成单条SQL语句执行的最大允许时间.它的值应小于JDBC Driver Socket Timeou

WorldWind源码剖析系列:漫游时四叉树瓦片类QuadTile的运行思路

用户在窗口漫游时,需要加载精细的高程和纹理数据时的处理思路:当用户漫游时直到窗口相机的视场角的1/2小于(瓦片大笑*瓦片绘制距离的乘积)时,才初始化四叉树瓦片类QuadTile,或者加载本地缓存中的数据,或者通过http请求下载远程的网络数据.接下来的处理流程为: 网络下载相关类之间的引用关系: 记住:在QuadTile::Initialize()和TerrainTile:: Initialize()这两个初始化方法中做了很多事情,如:判断影像瓦片数据或高程瓦片数据在本地缓存中是否存在,如果否,

[OpenGL] 基于纹理绘制的透明精灵

在二维游戏中,我们几乎绕不开精灵绘制这一过程,除了直接在opengl读入图像并绘制外,我们更常使用纹理来完成这一过程,把纹理贴到在xy平面上的面片,做出二维游戏的效果. 这样我们可以很方便的使用opengl提供给我们的一些方法来执行精灵的变换,而不是使用大量的贴图来手工完成变换过程:同时,还可以通过调节深度信息来确定物体的遮挡关系,而不用花心思考虑绘制的先后顺序,因为我们知道,在二维世界里,谁遮挡谁是和绘制顺序相关的. 但是,我们也发现,我们的精灵不总是四四方方的,所以纹理加载进来后会有背景色,

基于Flink秒级计算时CPU监控图表数据中断问题

基于Flink进行秒级计算时,发现监控图表中CPU有数据中断现象,通过一段时间的跟踪定位,该问题目前已得到有效解决,以下是解决思路: 一.问题现象 以SQL02为例,发现本来10秒一个点的数据,有时会出现断点现象,会少1-2个点甚至更多: 二.问题定位 针对该问题,根据数据处理链路,制定了数据输出跟踪示意图,如下所示: 通过输出的实际数据发现: 1.监控Agent的数据已经正确上报Kafka 2.从Kafka中可以正确取到监控Agent上报的数据 3.从计算完毕的Kafka中取不到丢失点的数据

基于jQuery实现页面滚动时顶部导航显示隐藏效果

<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel=&quo

【基础】Unity截图时只截取场景内容

在游戏截屏时,我们需要只截取场景内容,而不包括NGUI控件,实现脚本如下(只截取gameCamera和bgCamera中的内容): using UnityEngine; using System.Collections; public class CaptureTest : MonoBehaviour { public Camera gameCamera; public Camera bgCamera; // Use this for initialization void Start () {

基于本地文字提取有效定位和识别场景中的文字

一种无约束的端到端的文本定位和识别方法.首先用一个基于区域的方法对文本区域检测,然后更新文本区域用更加健壮(迭代)的方法.并且介绍一种基于笔画的方法,该方法有效的应对字符的大小变化, 旋转,即使只捕捉到文字的小片段. 场景识别又被称为是图片OCR.现在的滑窗技术的一个缺点是:窗口数会随着文本的不同大小旋转方向等因素急速增长.而基于连通域的方法对于一个连通域是一个单独字符还是一个字符的一部分处理的不好,而且对于若干字符粘连不可行.本文提出一种基于方法,受启发于:任意的字符都是有一些笔画组成.因此,

C# Asp.net MVC 分页时传参场景 动态生成QueryString

1. 获得url 2.解析query string,将外部传入的匿名对象的key/value添加进去 3. 添加该页面特殊的key/value 4.合并url var raw = HttpContext.Current.Request.Url.AbsoluteUri; string[] separateURL = raw.Split('?'); //if found "?" parse query string else return empty HttpValueCollection