原始作者:Clay Culver
中文译者:Antking
1)前提
2)简介
3)开始
4)摄象机
4.1 OGRE 摄象机
4.2 建立摄象机
5)视口(viewports)
5.1 OGRE 视口
5.2 建立一个视口
6)灯光和阴影
6.1 OGRE支持的阴影类型
6.2 在OGRE中使用阴影
6.3 灯光的种类
6.4 建立灯光
7)尝试
7.1 不同的阴影类型
7.2 灯光渐淡
7.3 SceneManager::setAmbientLight
7.4 视口背景颜色
7.5 Camera::setFarClipDistance
7.6 Planes(程度)
8)你的想法?
1)前提
这篇文章是假设你有C++编程知识并在编译器中安装和设置了OGRE。这篇文章是建立在第一篇教程的基础上的。
2)简介
在这篇教程中,我们将讲解更多的关于OGRE的结构。这篇教程主要是关于灯光物体的,并讲解了他们在OGRE中是如何建立阴影的。我们也将复习一下关于摄象机的知识。
通过这篇教程的学习,你将添加更多的代码到你的程序中,并看见他们的结果。
3)开始
和第一篇教程一样,我们将使用一些准备好了的代码。我们将在我们的TutorialApplication 类中添加两个方法:createViewport and createCamera。这两个函数也是在基本的ExampleApplication中定义了的,但是,在这篇教程中,我们将看一下摄象机和视口在实际中是如何工作的。
在编译器中建立一个工程,添加下面的代码到里面:
#include "ExampleApplication.h"
class TutorialApplication : public ExampleApplication
{
protected:
public:
TutorialApplication()
{
}
~TutorialApplication()
{
}
protected:
virtual void createCamera(void)
{
}
virtual void createViewports(void)
{
}
void createScene(void)
{
Entity *ent;
Light *light;
}
};
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
// Create application object
TutorialApplication app;
try {
app.go();
} catch( Exception& e ) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_IConERROR | MB_TASKMODAL);
#else
fprintf(stderr, "An exception has occured: %s/n", e.getFullDescription().c_str());
#endif
}
return 0;
}
如果你在WINDOWS下使用OGRE SDK,请添加"[OgreSDK_DIRECTORY]/samples/include" 下的目录到这个工程。如果你使用OGRE源代码,请更换成下面的路径"[OgreSource_DIRECTORY]/Samples/Common/include" 。请在继续下一段时,确信你的程序能运行,运行时,显示一个黑色的窗口。如果你在这里有问题,请参考《OGRE初学者引导》。
程序控制: 用WASD键移动,鼠标能环绕四周。ESCAPE键退出。
4)摄象机
4.1 OGRE 摄象机
下面,我们来建立查看场景的摄象机。摄象机是一个特别的物体,有点象SceneNode 。摄象机有setPosition,yaw,roll ,pitch4个函数,你能把他给予任何一个SceneNode 。和SceneNode 一样,摄象机的位置和他的父节点有关。对于移动和旋转摄象机,你应该象考虑SceneNode 一样考虑他。
值得注意的是,你可能想只能在场景中建立一个摄象机,这就是说,我们不能建立一个用于场景一部分的摄象机,而另一个用于场景的另一部分,然后根据场景的显示来确定使用哪个摄象机。相反,OGRE能建立很多的摄象机,把这些摄象机建立到SceneNode 中,然后根据场景显示时,根据他的SceneNode 点中的摄象机来决定场景的显示。我们将在FrameListener 教程中详细讨论他。
4.2 建立摄象机
我们将改变在ExampleApplication 中关于摄象机的方法。
找到TutorialApplication::createCamera 函数,因为摄象机是SceneManager 中的功能,因此,我们用SceneManager 来建立他们。添加下面的代码到程序中:
// create the camera
mCamera = mSceneMgr->createCamera("PlayerCam");
我们建立一个名叫PlayerCam"的摄象机。你能通过摄象机的名字,使用SceneManager 中的getCamera函数来得到摄象机,如果你不想把摄象机的指针给SceneManager 的话。
下面,我们将设置摄象机的位置和他的方向。因为物体初始时是在原点,因此我们的摄象机应该在+Z轴上变换,并使摄象机面向原点。添加下面代码到程序中:
// set its position, direction
mCamera->setPosition(Vector3(0,10,500));
mCamera->lookAt(Vector3(0,0,0));
你能使摄象机面向你调用yaw, rotate, and pitch 函数后的任何位置。
lookAt()在游戏中精确定位很重要。
它使SceneNodes在游戏中调整摄象机的方向很容易。最后,我们设置剪切距离为5。摄象机的剪切距离是指在你面前,你能看见的近处的或远处的物体。如果你把剪切距离的值设置的很小,你靠近物体,你不能全部看见,你只能看见物体的一小部分,相反,你将看见物体的全部。这在你将渲染大量的物体时很有用。下面代码设置最近的剪切距离:
mCamera->setNearClipDistance(5);
设置最远的剪切距离可以用setFarClipDistance。
5)视口(viewports)
5.1 OGRE 视口
当我们开始处理多个摄象机时,视口的概念就显的很重要。我认为明白OGRE是如何用摄象机来渲染场景是很重要。在OGRE中,多个SceneManagers 一起运行是可能的。他们也可能把屏幕分成多个方块,并且在屏幕上分别用摄象机渲染他们(例如,可以想象成有两个人在玩游戏)。关于这些是如何完成的,我们将在后面的教程中讲解。
要明白OGRE是如何渲染场景的,要明白OGRE的三个结构:the Camera, the SceneManager, and the RenderWindow。RenderWindow 我们以前没有讲过,但非常的简单,就是显示物体的窗口。
而SceneManager用于建立摄像机来显示场景。你必须告诉RenderWindow应该在屏幕上显示那个摄像机,在窗口中什么部分显示他。而你告诉RenderWindow显示摄像机的那个窗口部分就是你的视口。在OGRE的通常的用法中,你只需要建立一个摄像机,而这个摄像机就占有整个RenderWindow,这样就只有一个视口。
而在这章中,我们会讲解如何通过摄像机建立视口。我们能用这个视口去设置我们渲染的场景的背景颜色。
5.2 建立一个视口
让我们回到用ExampleApplication建立视口这个部分,找到TutorialApplication::createViewports 这个方法。要建立视口,我们只需要简单地调用RenderWindow中的addViewport方法,并用我们用的摄像机去支持它。mWindow类是从ExampleApplication类中派生出来的,因此,我们添加下面的代码:
// Create one viewport, entire window
Viewport* vp = mWindow->addViewport(mCamera);
现在,我们有了我们自己的视口,我们能用它做什么呢?最为重要的就是用它去调用setBackgroundColour方法去设置背景颜色。在这里,因为我们要处理灯光,因此,我们设置背景颜色为黑色。
vp->setBackgroundColour(Colourvalue(0,0,0));
注意颜色值是用红,绿和蓝来表示的,而这些值的取值范围为0到1。最后,我们要做的最为重要的事是设置摄像机的高宽比 。如果你设置标准全屏幕视口以外的模式,将得到一个很奇怪的场景。在这里,我们设置初始的高宽比:
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
6)灯光和阴影
6.1 OGRE支持的阴影类型
OGRE当前支持三种阴影:
1。调整纹理阴影(SHADOWTYPE_TEXTURE_MODULATIVE) - 是用的最常用的。这个用于建立黑白纹理阴影。
2,调整模版阴影(SHADOWTYPE_STENCIL_MODULATIVE) - 这种技术用于渲染所有在场景中不透明的物体。这种方法和附加模板阴影一样,不常用,但是很精确。
3,附加模板阴影 (SHADOWTYPE_STENCIL_ADDITIVE) - 这种技术用于渲染场景中、每个光线。这在图形卡上是非常困难的。
OGRE不将软阴影作为引擎的一部分。
6.2 在OGRE中使用阴影
在OGRE中使用阴影比较简单,SceneManager 中提供setShadowTechnique成员来设置阴影的类型。你也能用seCastShadows成员来设置投射阴影。
在这里,我们设置环境光为黑色,然后设置阴影。找到TutorialApplication::createScene成员,添加下面的代码:
mSceneMgr->setAmbientLight( Colourvalue( 0, 0, 0 ) );
mSceneMgr->setShadowTechnique( SHADOWTYPE_STENCIL_ADDITIVE );
现在 SceneManager用调整纹理阴影,下面,我们建立一个物体,并使它投射阴影:
ent = mSceneMgr->createEntity( "Ninja", "ninja.mesh" );
ent->setCastShadows( true );
mSceneMgr->getRootSceneNode()->createChildSceneNode( )->attachObject( ent );
在这里,ninja.mesh被 ExampleApplication预先导入。为了让ninja.mesh能显示投射阴影,我们必须为它建立一个支持阴影的面。
(译者注:下面这段文字可能有问题,原文如下:
Again, the ninja.mesh has been preloaded for us by the ExampleApplication. We also need something for the Ninja to stand on (so that he has something to cast shadows onto). To do this we will create a simple plane for him to stand on. This is not meant to be a tutorial on using MeshManager, but we will go over the very basics since we have to use it to create a plane. First we need to define the Plane (http://www.ogre3d.org/docs/api/html/classOgre_1_1Plane.html) object itself, which is done by supplying a normal and the distance from the origin. We could (for example) use planes to make up parts of world geometry, in which case we would need to specify something other than 0 for our origin distance. For now we just want a plane to have the positive y axis as its normal (that means we want it to face up), and no distance from the origin:
)
首先,我们必须为它建立一个PLANE对象,这个对象支持法线和来自原点的距离。我们可以使面成为世界几何物体的一部分,在这种情况下,我们将表明我们到原点的距离。但是现在,我们使面的法线的轴指向正Y轴,没有到原点的距离:
Plane plane( Vector3::UNIT_Y, 0 );
现在,我们将存储这个面,以便宜我们在程序中用到它。而MeshManager 类用于保持我们导入场景中的所有网格的路径。createPlane方法用于生成Plane的定义,并按照这些定义值生成网格。代码如下:
MeshManager::getSingleton().createPlane("ground",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z);
现在,也许你想知道MeshManager是如何使用的,但我现在并不想讲解。在这里,我们存储我们的面为1500X1500的网格,命名为“ground".下面建立这个网格并在场景中固定它:
ent = mSceneMgr->createEntity( "GroundEntity", "ground" );
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
在我们完成地面之前,我们需要做两件重要的事情。第一件事是告诉SceneManager 我们不想在不知道用什么阴影的情况下投射阴影。第二件事是地面的纹理。我们的机器人肯ninja 已经有材质脚本定义了。当我们建立我们的地面时,并没有为他建立文理。在这里我们用OGRE中的"Examples/Rockwall"中的材质脚本:
ent->setMaterialName("Examples/Rockwall");
ent->setCastShadows(false);
现在,我们在场景中有Ninja和地面,让我们编译和运行它。我们并没有看见任何物体!WHY?
那是因为我们没有添加灯光,下面让我们添加灯光。
6.3 灯光的种类
在OGRE中提供了三种灯光:
1,点(LT_POINT) - 从一点发出,向四面发散。
2,点光(LT_SPOTLIGHT) - 点光有点象电筒光。光从一点开始,然后朝一个方向发散。你也能定义点光的圆的角度,分为光源部分和散射部分。
3,方向光(LT_DIRECTIONAL) - 方向光有点象镜面反射光。例如,你建立了一个夜晚的场景,你想模拟月光。在这里,你能根据月亮的发射方向使用方向光来模拟月光。
光有许多参数,其中最为重要的是diffuse 和specular.每个材质脚本定义材质能反射多少diffuse 和specular光,在下面的章节中,我们将详细学习。
6.4 建立灯光
在OGRE中建立灯光,我们需要调用SceneManager中createLight方法。建立了灯光后,我们能设置它的位置或将它和SceneNode 联系起来,以便于移动。灯光只有两个函数用于设置:setPosition 和setDirection.因此,你想建立固定光,只需要调用setPosition方法。如果你想建立活动光,只需要将它和SceneNode联系起来。
下面,我们来建立点,第一步是建立灯光,设置类型和位置:
light = mSceneMgr->createLight( "Light1" );
light->setType( Light::LT_POINT );
light->setPosition( Vector3(0, 150, 250) );
我们已经建立了灯光,下面我们需要设置diffuse and specular颜色,这里设置红色:
light->setDiffuseColour( 1.0, 0.0, 0.0 );
light->setSpecularColour( 1.0, 0.0, 0.0 );
现在编译,运行,这时你能看见Ninja和它的阴影。注意我们并不能看见光源,而只能看见光源产生的结果。
下面,我们将建立方向光,在这里我们建立了黄色的方向光:
light = mSceneMgr->createLight( "Light3" );
light->setType( Light::LT_DIRECTIONAL );
light->setDiffuseColour( Colourvalue( .25, .25, 0 ) );
light->setSpecularColour( Colourvalue( .25, .25, 0 ) );
前面我们并没有设置方向光的方向,而只设置了它的位置,下面,我们将设置它的方向:指向正Z轴和负Y轴:
light->setDirection( Vector3( 0, -1, 1 ) );
编译和运行,你将在场景中得到两个阴影。最后,我们来建立点光:
light = mSceneMgr->createLight( "Light2" );
light->setType( Light::LT_SPOTLIGHT );
light->setDiffuseColour( 0, 0, 1.0 );
light->setSpecularColour( 0, 0, 1.0 );
我们同样需要设置点光的位置和方向:
light->setDirection( -1, -1, 0 );
light->setPosition( Vector3( 300, 300, 0 ) );
想象一下,电筒可以通过调节光圈来控制光的范围。同样点光可以通过setSpotlightRange方法来调节:
light->setSpotlightRange( Degree(35), Degree(50) );
7)尝试
7.1 不同的阴影类型
在这篇文章中,我们只使用了阴影的SHADOWTYPE_STENCIL_ADDITIVE类型,请你试试其他两种类型,看看发生了什么。在SceneManager中还有许多其他关于阴影的函数。试试它们,看看你能否使用它们。
7.2 灯光渐淡
灯光中还提供了一个setAttenuation,用于减淡灯光。将这个函数加入你的点光中,设置不同的值,看看有什么效果。
7.3 SceneManager::setAmbientLight
测试一下SceneManager中的setAmbientLight方法。
7.4 视口背景颜色
在createViewports函数中改变颜色值。学会如何去改变它,以便于更好的使用它。
7.6 Planes(程度)
在这章中,我们并没有详细讲解Planes(程度),我们将在下一课讲解它。在这里,你主要是学会如何使用createPlane函数。