在玩一些3D游戏的时候,经常会有画中画的功能,比如“跑跑卡丁车”、“杀手十三”等等,于是想自己动手试验一下。
普通情况下,我们使用单一的摄像机,实现第一人称或者第三人称,有时我们用多个摄像机在单一的窗口中切换视角,比如从第一人称切换到第三人称视角(游戏中屡见不鲜),而画中画将会同时展现第一人称和第三人称(或者第三人称与第三人称),也就是说同时存在不同的观察点,这就需要多窗口多摄像机分别进行渲染。
用Ogre实现画中画功能是件比较容易的事情,首先,搭建一个普通的场景,关于这方面的工作
www.ogre3d.org/wiki里有详细的教程,不赘述了。
下面进入关键部分。
在Ogre里摄像机Camera和视口Viewport是一一对应的关系,普通情况下包含一个摄像机对应一个视口,我们只要添加摄像机和与之对应的视口就OK了!
我的窗口类里是这样配置Ogre的,基本和教程示例里的一样。
bool BaseApplication::setup(void)
{
mRoot = new Root();
// 设置资源
setupResources();
// 配置渲染窗口
bool carryOn = configure();
if (!carryOn)
return false;
// 创建场景管理器
chooseSceneManager();
// 创建摄像机
createCamera();
// 创建视口
createViewports();
// 设置缺省 mipmap 等级
TextureManager::getSingleton().setDefaultNumMipmaps(5);
// 创建所有资源监听器(为了加载屏幕)
createResourceListener();
// 加载资源
loadResources();
// 创建场景
createScene();
// 创建帧监听器
createFrameListener();
return true;
}
其中,createCamera(); // 创建摄像机 和 createViewports(); // 创建视口 是我们需要关心的。
createCamera():
void BaseApplication::createCamera(void)
{
// 主窗口摄像机
mCamera_1 = mSceneMgr->createCamera("Cam_1");
mCamera_1->setPosition(Vector3(0,0,300));
mCamera_1->lookAt(Vector3(0,0,-300));
mCamera_1->setNearClipDistance(5);
// 画中画摄像机
mCamera_2 = mSceneMgr->createCamera("Cam_2");
mCamera_2->setPosition(Vector3(100,100,300));
mCamera_2->lookAt(Vector3(-100,-100,-300));
mCamera_2->setNearClipDistance(5);
}
createViewports():
void BaseApplication::createViewports(void)
{
// 主窗口
Viewport* vp_1 = mWindow->addViewport( mCamera_1 );
vp_1->setBackgroundColour( ColourValue(0,0,0) );
mCamera_1->setAspectRatio(
Real( vp_1->getActualWidth() ) / Real( vp_1->getActualHeight() ) );
// 画中画
Viewport* vp_2 = mWindow->addViewport( mCamera_2, 1, 0.7, 0.05, 0.25, 0.25 );
vp_2->setBackgroundColour( ColourValue(0,0,0) );
vp_2->setOverlaysEnabled(false);
mCamera_2->setAspectRatio(
Real( vp_2->getActualWidth() ) / Real(vp_2->getActualHeight() ) );
}
在addViewport中我们控制画中画视口在主窗口中的位置和大小(注意这里是0~1的取值范围,类似于贴图坐标)
这样就实现了下图所示效果的一半了。
是不是很酷?如果进一步设计成,当触发某一事件时将画中画窗口动态的弹出,那就更酷了!有兴趣的可以试一试:P
如图所示,我另外还利用了CEGUI给画中画窗口加了个边框,并且带了一个combobox用来控制更多摄像机之间的切换,CEGUI是用脚本来定义界面的,实现也比较简单。有关CEGUI部分的介绍,在ogre wiki上有更详细的教程。
#end