您的位置:首页 > 运维架构

opengles拾取,碰撞检测,相交检测

2017-06-05 10:20 387 查看




本例用AABB来实现opengl中的拾取功能,AABB在opengl中可用来做相交检测,碰撞检测,本例用于检测鼠标指向的射线是否和物体相交

AABB实现类

template<typename T>
class AxisAlignedBox
{
public:
enum Extent
{
EXTENT_NULL, //是否被初始化
EXTENT_FINITE,//有限大
EXTENT_INFINITE//无限大
};
public:
tvec3<T> _minimum;//最小值
tvec3<T> _maximum;//最大值
Extent _extent;//扩展变量
public:
/*
1-----2
/| /|
/ | / |
5-----4 |
| 0--|--3
| / | /
|/ |/
6-----7
*/
typedef enum
{
FAR_LEFT_BOTTOM = 0,
FAR_LEFT_TOP = 1,
FAR_RIGHT_TOP = 2,
FAR_RIGHT_BOTTOM = 3,
NEAR_RIGHT_BOTTOM = 7,
NEAR_LEFT_BOTTOM = 6,
NEAR_LEFT_TOP = 5,
NEAR_RIGHT_TOP = 4
} CornerEnum;

AxisAlignedBox()
{
_minimum = tvec3<T>( T(-0.5), T(-0.5), T(-0.5) );
_maximum = tvec3<T>( T(0.5), T(0.5), T(0.5) );
_extent = EXTENT_NULL;
}
AxisAlignedBox(const AxisAlignedBox & rkBox)
{
setExtents( rkBox._minimum, rkBox._maximum );
_extent = rkBox._extent;
}

AxisAlignedBox( const tvec3<T>& min, const tvec3<T>& max )
{
setExtents( min, max );
}

AxisAlignedBox(
T mx, T my, T mz,
T Mx, T My, T Mz
)
{
setExtents( mx, my, mz, Mx, My, Mz );
}

AxisAlignedBox<T>& operator=(const AxisAlignedBox<T>& right)
{
setExtents(right._minimum, right._maximum);
return *this;
}

~AxisAlignedBox()
{
}
/**
* 获取最小值
*/
const tvec3<T>& getMinimum(void) const
{
return _minimum;
}

/**
* Gets a modifiable version of the minimum
* corner of the box.
*/
tvec3<T>& getMinimum(void)
{
return _minimum;
}

//设置最小值
void setMinimum(const tvec3<T>& mins)
{
_minimum = mins;
}
void setMinimum(T x,T y, T z)
{
_minimum = tvec3<T>(x,y,z);
}
/**
* Gets the maximum corner of the box.
*/
const tvec3<T>& getMaximum(void) const
{
return _maximum;
}
/**
* Gets a modifiable version of the maximum
* corner of the box.
*/
tvec3<T>& getMaximum(void)
{
return _maximum;
}
/**
* Sets the maximum corner of the box.
*/
void setMaximum( const tvec3<T>& vec )
{
_maximum = vec;
}

void setMaximum( T x, T y, T z )
{
_maximum.x = x;
_maximum.y = y;
_maximum.z = z;
}

/**
* Changes one of the components of the maximum corner of the box
* used to resize only one dimension of the box
*/
void setMaximumX( T x )
{
_maximum.x = x;
}

void setMaximumY( T y )
{
_maximum.y = y;
}

void setMaximumZ( T z )
{
_maximum.z = z;
}

/**
* 设置包围盒的大小 min:最小值 max:最大值
*/
void setExtents( const tvec3<T>& min, const tvec3<T>& max )
{
_minimum = min;
_maximum = max;
_extent = EXTENT_FINITE;
}
//设置包围盒大小 mx my mz 最小值xyz Mx My Mz 最大值xyz
void setExtents(
T mx, T my, T mz,
T Mx, T My, T Mz )
{
_minimum.x = mx;
_minimum.y = my;
_minimum.z = mz;

_maximum.x = Mx;
_maximum.y = My;
_maximum.z = Mz;
_extent = EXTENT_FINITE;

}

/** Returns a pointer to an array of 8 corner points, useful for
collision vs. non-aligned objects.
@remarks
If the order of these corners is important, they are as
follows: The 4 points of the minimum Z face (note that
because Ogre uses right-handed coordinates, the minimum Z is
at the 'back' of the box) starting with the minimum point of
all, then anticlockwise around this face (if you are looking
onto the face from outside the box). Then the 4 points of the
maximum Z face, starting with maximum point of all, then
anticlockwise around this face (looking onto the face from
outside the box). Like this:
<pre>
1-----2
/| /|
/ | / |
5-----4 |
| 0--|--3
| / | /
|/ |/
6-----7
</pre>
@remarks as this implementation uses a static member, make sure to use your own copy !

获取包围盒的8个顶点坐标
*/
void getAllCorners(tvec3<T> mpCorners[8] ) const
{
mpCorners[0] = _minimum;
mpCorners[1].x = _minimum.x; mpCorners[1].y = _maximum.y; mpCorners[1].z = _minimum.z;
mpCorners[2].x = _maximum.x; mpCorners[2].y = _maximum.y; mpCorners[2].z = _minimum.z;
mpCorners[3].x = _maximum.x; mpCorners[3].y = _minimum.y; mpCorners[3].z = _minimum.z;

mpCorners[4] = _maximum;
mpCorners[5].x = _minimum.x; mpCorners[5].y = _maximum.y; mpCorners[5].z = _maximum.z;
mpCorners[6].x = _minimum.x; mpCorners[6].y = _minimum.y; mpCorners[6].z = _maximum.z;
mpCorners[7].x = _maximum.x; mpCorners[7].y = _minimum.y; mpCorners[7].z = _maximum.z;
}

/**
* gets the position of one of the corners
*/
tvec3<T> getCorner(CornerEnum cornerToGet) const
{
switch(cornerToGet)
{
case FAR_LEFT_BOTTOM:
return _minimum;
case FAR_LEFT_TOP:
return tvec3<T>(_minimum.x, _maximum.y, _minimum.z);
case FAR_RIGHT_TOP:
return tvec3<T>(_maximum.x, _maximum.y, _minimum.z);
case FAR_RIGHT_BOTTOM:
return tvec3<T>(_maximum.x, _minimum.y, _minimum.z);
case NEAR_RIGHT_BOTTOM:
return tvec3<T>(_maximum.x, _minimum.y, _maximum.z);
case NEAR_LEFT_BOTTOM:
return tvec3<T>(_minimum.x, _minimum.y, _maximum.z);
case NEAR_LEFT_TOP:
return tvec3<T>(_minimum.x, _maximum.y, _maximum.z);
case NEAR_RIGHT_TOP:
return _maximum;
default:
return tvec3<T>();
}
}

/**
* Merges the passed in box into the current box. The result is the
* box which encompasses both.

合并包围盒 两个包围盒合并到一起,就是取并集
*/
void merge( const AxisAlignedBox<T>& right )
{

if ((right._extent == EXTENT_NULL) || (_extent == EXTENT_INFINITE))
{
return;
}
else if (right._extent == EXTENT_INFINITE)
{
_extent = EXTENT_INFINITE;
}
else if (_extent == EXTENT_NULL)
{
setExtents(right._minimum, right._maximum);
}
else
{
//! merge
tvec3<T> min = _minimum;
tvec3<T> max = _maximum;
max.makeCeil(right._maximum);//取最大值
min.makeFloor(right._minimum);//取最小值
setExtents(min, max);
}
}

/**
* Extends the box to encompass the specified point (if needed).
*/
void merge( const tvec3<T>& point )
{
switch (_extent)
{
case EXTENT_NULL: // if null, use this point
setExtents(point, point);
return;

case EXTENT_FINITE:
_maximum.makeCeil(point);
_minimum.makeFloor(point);
return;

case EXTENT_INFINITE:
return;
}
}
/*
模型变换了,从新计算最大值/最小值
正方形6个面一共36个顶点,再算出最大值和最小值,
再和模型相乘
原理:8个顶点和矩阵相乘,得到新的8个顶点,再求最大值和最小值
matrix:模型矩阵
*/
void transform( const tmat4x4<T>& matrix )
{
tvec3<T> oldMin;
tvec3<T> oldMax;
tvec3<T> currentCorner;

oldMin = _minimum;
oldMax = _maximum;

// We sequentially compute the corners in the following order :
// 0, 6, 5, 1, 2, 4 ,7 , 3
// This sequence allows us to only change one member at a time to get at all corners.

// For each one, we transform it using the matrix
// Which gives the resulting point and merge the resulting point.

currentCorner = oldMin;
tvec3<T> vVert = currentCorner * matrix;
setExtents(vVert,vVert);

// First corner
// min min min
currentCorner = oldMin;
merge( currentCorner * matrix );

// min,min,max
currentCorner.z = oldMax.z;
merge( currentCorner * matrix );

// min max max
currentCorner.y = oldMax.y;
merge( currentCorner * matrix );

// min max min
currentCorner.z = oldMin.z;
merge( currentCorner * matrix );

// max max min
currentCorner.x = oldMax.x;
merge( currentCorner * matrix );

// max max max
currentCorner.z = oldMax.z;
merge( currentCorner * matrix );

// max min max
currentCorner.y = oldMin.y;
merge( currentCorner * matrix);

// max min min
currentCorner.z = oldMin.z;
merge( currentCorner * matrix);
}

/**
* 和盒子相交(两个盒子相交)
b2:第二个盒子
return true 相交 false 不相交
*/
bool intersects(const AxisAlignedBox& b2) const
{
if (_maximum.x < b2._minimum.x)
return false;
if (_maximum.y < b2._minimum.y)
return false;
if (_maximum.z < b2._minimum.z)
return false;

if (_minimum.x > b2._maximum.x)
return false;
if (_minimum.y > b2._maximum.y)
return false;
if (_minimum.z > b2._maximum.z)
return false;
return true;

}

/**
* 和盒子相交,不考虑z轴(两个盒子相交)
b2 第二个盒子
return true 相交 false 不想交
*/
bool intersectsNoZ(const AxisAlignedBox& b2) const
{
if (_maximum.x < b2._minimum.x)
return false;
if (_maximum.y < b2._minimum.y)
return false;

if (_minimum.x > b2._maximum.x)
return false;
if (_minimum.y > b2._maximum.y)
return false;
return true;

}

/*
和盒子相交 (两个盒子相交)
b2:第二个盒子
return 相交集
*/
AxisAlignedBox<T> intersection(const AxisAlignedBox<T>& b2) const
{
tvec3<T> intMin = _minimum;
tvec3<T> intMax = _maximum;

intMin.makeCeil(b2.getMinimum());
intMax.makeFloor(b2.getMaximum());

if (intMin.x < intMax.x &&
intMin.y < intMax.y &&
intMin.z < intMax.z)
{
return AxisAlignedBox<T>(intMin, intMax);
}

return AxisAlignedBox<T>();
}
void setNull()
{
_extent = EXTENT_NULL;
}

//是否被初始化
bool isNull(void) const
{
return (_extent == EXTENT_NULL);
}

//是否有限的
bool isFinite(void) const
{
return (_extent == EXTENT_FINITE);
}

void setInfinite()
{
_extent = EXTENT_INFINITE;
}
bool isInfinite(void) const
{
return (_extent == EXTENT_INFINITE);
}

void scale(const tvec3<T>& s)
{
tvec3<T> min = _minimum * s;
tvec3<T> max = _maximum * s;
setExtents(min, max);
}
/*
和点相交 三维点
*/
bool intersects(const tvec3<T>& v) const
{
return( v.x >= _minimum.x && v.x <= _maximum.x &&
v.y >= _minimum.y && v.y <= _maximum.y &&
v.z >= _minimum.z && v.z <= _maximum.z);
}

/*和点相交 三维点*/
bool intersects(const tvec2<T>& v) const
{
return( v.x >= _minimum.x && v.x <= _maximum.x &&
v.y >= _minimum.y && v.y <= _maximum.y );
}

//获取包围盒中心点
tvec3<T> getCenter(void) const
{
return tvec3<T>(
(_maximum.x + _minimum.x) * T(0.5f),
(_maximum.y + _minimum.y) * T(0.5f),
(_maximum.z + _minimum.z) * T(0.5f)
);
}
/**
* 获取包围盒大小
*/
tvec3<T> getSize(void) const
{
return _maximum - _minimum;
}

/*
获取包围盒一半的大小
*/
tvec3<T> getHalfSize(void) const
{
return (_maximum - _minimum) * T(0.5);
}

//是否包含点
bool contains(const tvec3<T>& v) const
{
return _minimum.x <= v.x && v.x <= _maximum.x &&
_minimum.y <= v.y && v.y <= _maximum.y &&
_minimum.z <= v.z && v.z <= _maximum.z;
}

//是否包含盒子
bool contains(const AxisAlignedBox& other) const
{
return this->_minimum.x <= other._minimum.x &&
this->_minimum.y <= other._minimum.y &&
this->_minimum.z <= other._minimum.z &&
other._maximum.x <= this->_maximum.x &&
other._maximum.y <= this->_maximum.y &&
other._maximum.z <= this->_maximum.z;
}
bool operator== (const AxisAlignedBox& right) const
{
return this->_minimum == right._minimum &&
this->_maximum == right._maximum;
}
bool operator!= (const AxisAlignedBox& right) const
{
return !(*this == right);
}
};获取要包围模型的极值点
float3 vMin(FLT_MAX,FLT_MAX,FLT_MAX);
float3 vMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
//遍历要包围的模型的所有顶点,获取最大值和最小值
for (size_t i = 0; i < 36; i++)
{
vMin.x = min(boxVertex[i].x,vMin.x);
vMin.y = min(boxVertex[i].y,vMin.y);
vMin.z = min(boxVertex[i].z,vMin.z);

vMax.x = max(boxVertex[i].x,vMax.x);
vMax.y = max(boxVertex[i].y,vMax.y);
vMax.z = max(boxVertex[i].z,vMax.z);
}
//设置包围盒的大小
_aabbBox.setExtents(vMin,vMax);

}渲染函数
virtual void onRender(const FrameEvent&, int width, int height)
{
//清除深度缓冲和颜色缓冲
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
//设置视口大小
glViewport(0,0,width,height);

_camera.setViewSize(width,height);
_role.render(1.0f/60.f);
_camera.setTarget(_role._pos);
_camera.update();

CELL::matrix4 matModel;
matModel.translate(_role._pos);

//摄像机观察矩阵
CELL::matrix4 matView = _camera.getView();
//投影矩阵
CELL::matrix4 matProj = CELL::perspective<float>(45.0f, float(width) / float(height), 0.1f, 1000.0f);
_camera.setProject(matProj);

CELL::matrix4 MVP = matProj*matView;

float gSize = 100;
float gPos = 0;
float rept = 100;

Vertex grounds[] =
{
{ -gSize, gPos, -gSize, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0, 1, 0 },
{ gSize, gPos, -gSize, rept, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0, 1, 0 },
{ gSize, gPos, gSize, rept, rept, 1.0f, 1.0f, 1.0f, 1.0f, 0, 1, 0 },

{ -gSize, gPos, -gSize, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0, 1, 0 },
{ gSize, gPos, gSize, rept, rept, 1.0f, 1.0f, 1.0f, 1.0f, 0, 1, 0 },
{ -gSize, gPos, gSize, 0.0f, rept, 1.0f, 1.0f, 1.0f, 1.0f, 0, 1, 0 },
};

_device.bindTexture2D(&_texture);
_shader.begin();
{
_device.setUniformMatrix4fv(_shader._MVP,1,false,MVP.data());
_device.setUniform1i(_shader._texture,0);
_device.attributePointer(_shader._positionAttr,3,GL_FLOAT,false,sizeof(Vertex),&grounds[0].x);
_device.attributePointer(_shader._uvAttr, 2, GL_FLOAT, false, sizeof(Vertex), &grounds[0].u);
_device.attributePointer(_shader._colorAttr, 4, GL_FLOAT, false, sizeof(Vertex), &grounds[0].r);

_device.drawArray(GL_TRIANGLES,0,6);
_device.setLineWidth(5);

_device.attributePointer(_shader._positionAttr, 3, GL_FLOAT, false, sizeof(Vertex), &_ptLine[0].x);
_device.attributePointer(_shader._uvAttr, 2, GL_FLOAT, false, sizeof(Vertex), &_ptLine[0].u);
_device.attributePointer(_shader._colorAttr, 4, GL_FLOAT, false, sizeof(Vertex), &_ptLine[0].r);
_device.drawArray(GL_LINES, 0, 2);

}
_shader.end();

_lighting.begin();
{
//旋转矩阵
CELL::matrix4 matRot(1);
CELL::matrix4 matModel;
matModel.translate(_role._pos);
static float agle = 0;
matRot.rotateYXZ(agle,agle,agle);
agle += 1.0f;
MVP = matProj*matView*(matModel*matRot);

CELL::matrix4 mv = matView*(matModel*matRot);
CELL::matrix4 mvInv = mv.inverse();//求逆矩阵

_aabbTran = _aabbBox;
CELL::matrix4 matTra = matModel*matRot;
//模型变换后重新计算极值
_aabbTran.transform(matTra);

float3 arBox[8];
Vertex vertexBox[8];
//获取包围盒的8个顶点
_aabbTran.getAllCorners(arBox);
for (size_t i = 0; i <8; i++)
{
vertexBox[i].x = arBox[i].x;
vertexBox[i].y = arBox[i].y;
vertexBox[i].z = arBox[i].z;
}
//顶点索引
short boxIndex[24]=
{
0, 1, 2, 3,
2, 3, 7, 4,
4, 5, 6, 7,
0, 1, 5, 6,
1, 2, 4, 5,
0, 3, 7, 6
};

float fEyePosition[] = {_camera._eye.x,_camera._eye.y,_camera._eye.z,0.0f};
float fLightVector[] = {0.0f,1.0f,0.0f,0.0f};

//归一化
float fLength = sqrtf(fLightVector[0] * fLightVector[0] +
fLightVector[1] * fLightVector[1] +
fLightVector[2] * fLightVector[2]);
fLightVector[0] /= fLength;
fLightVector[1] /= fLength;
fLightVector[2] /= fLength;

//绘制地面
glUniformMatrix4fv(_lighting.modelViewProjection,1,false,MVP.data());
glUniformMatrix4fv(_lighting.modelViewInverse,1,false,mvInv.data());

glUniform3fv(_lighting.eyePosition,1,fEyePosition);
glUniform3fv(_lighting.lightVector,1,fLightVector);

glVertexAttribPointer(_lighting._positionAttr,3,GL_FLOAT,false,sizeof(Vertex),&boxVertex[0].x);
glVertexAttribPointer(_lighting._normal, 3, GL_FLOAT, false, sizeof(Vertex), &boxVertex[0].nx);

glDrawArrays(GL_TRIANGLES,0,36);

MVP = matProj*matView;
glUniformMatrix4fv(_lighting.modelViewProjection,1,false,MVP.data());

glVertexAttribPointer(_lighting._positionAttr,3,GL_FLOAT,false,sizeof(Vertex),&vertexBox[0].x);
glVertexAttribPointer(_lighting._normal,3,GL_FLOAT,false,sizeof(Vertex),&vertexBox[0].nx);

glDrawElements(GL_LINE_STRIP,4,GL_UNSIGNED_SHORT,&boxIndex[0]);
glDrawElements(GL_LINE_STRIP, 4, GL_UNSIGNED_SHORT, &boxIndex[4]);
glDrawElements(GL_LINE_STRIP, 4, GL_UNSIGNED_SHORT, &boxIndex[8]);
glDrawElements(GL_LINE_STRIP, 4, GL_UNSIGNED_SHORT, &boxIndex[12]);
glDrawElements(GL_LINE_STRIP, 4, GL_UNSIGNED_SHORT, &boxIndex[16]);
glDrawElements(GL_LINE_STRIP, 4, GL_UNSIGNED_SHORT, &boxIndex[20]);

}
_lighting.end();

}

鼠标按下时检测射线和模型的AABB是否相交
virtual void onMousePress(int absx, int absy, CELL::MouseButton id)
{

if (id == CELL::MouseButton::Left)
{
Ray ray = _camera.createRayFromScreen(absx,absy);

float3 pos = ray.getOrigin();
float tm = abs((pos.y) / ray.getDirection().y);
float3 target = ray.getPoint(tm);
_role.setTarget(float3(target.x,0,target.z));

_ptLine[0].x = pos.x;
_ptLine[0].y = pos.y;
_ptLine[0].z = pos.z;

_ptLine[1].x = target.x;
_ptLine[1].y = 0;
_ptLine[1].z = target.z;

//测试射线和box是否相交
if (ray.intersects(_aabbTran).first)
{
int i = 0;
}
else
{
int ss = 0;
}

}
else if( id== CELL::MouseButton::Right)
{
_mousePos = float2(absx,absy);
_rightButtonDown = true;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: