Cocos2d-x 3.x 图形学渲染系列八

笔者介绍:姜雪伟IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

3D游戏引擎开发中,都会使用虚拟摄像机,虚拟摄像机的裁剪是通过视景体实现的,视景体代码在Cocos2d-x引擎中已经实现了出来了,下面就给读者介绍一下:

视景体在任何3D游戏中都会使用,不管如何封装,它们的实现原理都是一样的,既是根据摄像机生成一个立体的框用于场景裁剪。在3D游戏中,相机的裁剪都需要视景体,实景体的主要作用是对摄像机可视范围外的物体进行裁剪,视景体函数的主要作用是根据AABB碰撞盒和OBB碰撞盒对在视景体外的物体做裁剪处理,CCFrustum的核心函数是创建视景体和判断是否在视景体内外的物体进行处理,游戏中的摄像机的视野不是无限的,必须为它制定一个视景体,在视景体内的物体将被投影到视平面,不在视景体内的物体将被丢弃不处理。视景体的效果给读者展示一下:


该图便是一个视景体,这个投影是透视投影,透视投影就是给人置中于场景中的感觉,它产生的效果就是远处的物体显得小,近处的物体显得大。

cocos2d-x引擎中首先实现的是创建视景体的立方体平面,在引擎中的实现创建平面的代码如下所示:

void Frustum::createPlane(const Camera* camera)
{
	const Mat4& mat = camera->getViewProjectionMatrix();
	//ref http://www.lighthouse3d.com/tutorials/view-frustum-culling/clip-space-approach-extracting-the-planes/
	//extract frustum plane
	_plane[0].initPlane(-Vec3(mat.m[3] + mat.m[0], mat.m[7] + mat.m[4], mat.m[11] + mat.m[8]), (mat.m[15] + mat.m[12]));//left
	_plane[1].initPlane(-Vec3(mat.m[3] - mat.m[0], mat.m[7] - mat.m[4], mat.m[11] - mat.m[8]), (mat.m[15] - mat.m[12]));//right
	_plane[2].initPlane(-Vec3(mat.m[3] + mat.m[1], mat.m[7] + mat.m[5], mat.m[11] + mat.m[9]), (mat.m[15] + mat.m[13]));//bottom
	_plane[3].initPlane(-Vec3(mat.m[3] - mat.m[1], mat.m[7] - mat.m[5], mat.m[11] - mat.m[9]), (mat.m[15] - mat.m[13]));//top
	_plane[4].initPlane(-Vec3(mat.m[3] + mat.m[2], mat.m[7] + mat.m[6], mat.m[11] + mat.m[10]), (mat.m[15] + mat.m[14]));//near
	_plane[5].initPlane(-Vec3(mat.m[3] - mat.m[2], mat.m[7] - mat.m[6], mat.m[11] - mat.m[10]), (mat.m[15] - mat.m[14]));//far
}

下面开始对视景体内的物体进行判断操作了,判断物体是否在视景体内有两种方法:一是利用AABB碰撞盒判断函数:

bool Frustum::isOutOfFrustum(const AABB& aabb) const
{
   if (_initialized)
    {
	Vec3 point;

	int plane = _clipZ ?6 :4;
	for (int i = 0; i < plane; i++)
        {
	    const Vec3& normal = _plane[i].getNormal();
            point.x = normal.x<0 ? aabb._max.x : aabb._min.x;
	    point.y = normal.y<0 ? aabb._max.y : aabb._min.y;
	    point.z = normal.z<0 ? aabb._max.z : aabb._min.z;

	    if (_plane[i].getSide(point) == PointSide::FRONT_PLANE )
		return	true;
        }
    }
	return	false;
}

它的实现原理是通过判断视景体平面与物体的碰撞盒面之间是否有碰撞检测,如果没有则物体在视景体内,返回true,否则返回false。另一种是利用OBB碰撞盒算法判断,函数如下所示:

bool Frustum::isOutOfFrustum(const OBB& obb) const
{
         if (_initialized)
         {
		Vec3 point;
		int plane = _clipZ ?6 :4;
		Vec3 obbExtentX = obb._xAxis * obb._extents.x;
		Vec3 obbExtentY = obb._yAxis * obb._extents.y;
		Vec3 obbExtentZ = obb._zAxis * obb._extents.z;

		for (int i = 0; i < plane; i++)
                {
			const Vec3& normal = _plane[i].getNormal();
	 		point = obb._center;
           		 point = normal.dot(obb._xAxis) >0 ? point - obbExtentX : point + obbExtentX;
          		 point = normal.dot(obb._yAxis) >0 ? point - obbExtentY : point + obbExtentY;
           		 point = normal.dot(obb._zAxis) >0 ? point - obbExtentZ : point + obbExtentZ;

			if (_plane[i].getSide(point) == PointSide::FRONT_PLANE)
				return true;
       	 	}
   	 }
		return false;
}

它的实现原理是通过视景体面的法线与OBB中的轴进行点乘判断物体是否在视景体内,如果在视景体内则返回true,否则返回false。上述两种算法主要是提供了用于判断物体是否在视景体内以便于裁剪。下面给读者展示一下裁剪的效果如下所示:


场景中的物体是AABB碰撞盒,视景体根据这些碰撞盒进行了裁剪操作,视野外面的物体被裁剪掉了,希望对读者有所帮助。


已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页