自己学习OGRE的笔记(1-5)

原文参考:http://lyme.blog.sohu.com/entry/1340641/#entry

感觉写的还不错,尤其是第5篇值得经常阅读。因为市面上大部分包括官方的教程都很浅显的教会你怎么用,感觉总有一点不确信的感觉。

 

 

自己学习OGRE的笔记1

分类: 日记 2007-06-01 22:27

    OGRE的中文教学资源很少,所以打算自己写一点,不过首先声明我在这方面也差不多是菜鸟。本文主要是翻译和记录了Apress出版的Pro OGRE 3D Programming的一些重要部分,这本书的电子书我已经上传到CSDN中我的资源里,需要者自己去那里下载。

http://download.csdn.net/source/175364

OGRE基本设定入门

Ogre的渲染序列:

    地形/世界物体(terrain and/or world geometry),可移动物体(movable objects),特效(effects),界面(overlays),然后是背景和空间盒(backgrounds and/or skyboxes)

Ogre支持的动画类型:

    骨骼/蒙皮(skeletal),变形(morph),(pose)

    其中蒙皮动画尚不支持IK解算,要抽取并转化为关键帧动画来实现,一般来说,Ogre的导入器会为你做这些。

    Morph和Pose的区别在于Morph使用绝对坐标计算形变,而Pose使用单个点的轨迹计算形变。Morph和Pose不能混合使用,但是他们都能和skeletal混用。

    使用Ogre之前的第一件事是创建一个root的场景对象(create an instance of root)。这些参数的命名类似于:

Root *root = new Root();

Root *root = new Root("plugins.cfg");

Root *root = new Root("plugins.cfg", "ogre.cfg");

Root *root = new Root("plugins.cfg", "ogre.cfg", "ogre.log");

Root *root = new Root("", "");

    Ogre中的plugin指的是所有提供用户界面的代码模块,诸如SceneManager和RenderSystem等等。关于plugin的连接和定义方式:

# Define plugin folder

PluginFolder=.

# Define plugins

Plugin=RenderSystem_Direct3D9

Plugin=RenderSystem_GL

Plugin=Plugin_ParticleFX

Plugin=Plugin_BSPSceneManager

Plugin=Plugin_OctreeSceneManager

Plugin=Plugin_CgProgramManager

    你可以在任何你想要的时刻调用它,并把命名提供给root constructor。如果你不设定这个参数(比如 Root *root = new Root();),root会按默认名“plugins.cfg”在你的软件安装目录下寻找配置文件。如果你设定空的参数(比如 Root *root = new Root("", "");),那么root将不载入配置文件,你将需要自己手动载入。

    PluginFolder设定了插件的路径,你可以设定绝对路径,也可以设定相对路径。如果你不设定这个路径,在windows下它将在你当前程序的驱动盘下找,在linux下它将会在root文件夹里找。

 

    调用默认配置对话框的方法是:

Root *root = new Root();

bool rtn = root->showConfigDialog();

    以上代码将产生ogre.cfg文件。如果要使用已经存在的ogre.cfg配置文件,可以使用以下代码:

if (!root->restoreConfig())

root->showConfigDialog();

    你还可以使用以下代码来保存一个你自己命名的配置文件(比如说 myapp.cfg):

root->saveConfig();

    Ogre log可以帮助你记录程序的出错信息,创建一个名为mylog.log的方法如下:

// create an instance of LogManager prior to using LogManager::getSingleton()

LogManager* logMgr = new LogManager;

Log *log = LogManager::getSingleton().createLog("mylog.log", true, true, false);

// third param is not used since we already created a log in the previous step

Root *root = new Root("", "");

    初始化Root的代码:

root->initialise(true, "My Render Window");

RenderWindow *window = root->getAutoCreatedWindow();

    第一行是完成Root的初始化并且创建一个名为My Render Window的渲染窗口,其中第一个参数true是让Ogre自动创建一个渲染窗口。第二行创建了一个指向RenderWindow的指针。

    创建摄像机的代码如下:

Camera *cam = sceneMgr->createCamera("MainCamera");

cam->setNearClipDistance(5);

cam->setFarClipDistance(1000);

cam->setAspectRatio(Real(1.333333));

    该代码第一行创建了一个摄像机和一个指向它的指针,第二和第三行设定了近切片和远切片的数值范围,最后一行定义了视图的比例,通常普通的显示器长和宽的比例是4:3,因此近似设为1.333333,当然宽屏的显示器应该设为1.6左右。关于近切片和远切片的说明:

 A popular misconception is that clip planes are a cheap method to reduce the amount of “stuff” that a card has to render. While this certainly is a side effect of clip distance selection (and most modern cards support infinite far clip planes anyway), the primary reason for a particular set of clip plane distances is to maintain optimal depth buffer resolution. The depth buffer resolution is a direct function of the ratio between the far and near clip distances, and selecting distances that result in too coarse a resolution will invariably result in depth fighting. This phenomenon occurs when the depth-sorting algorithm on the GPU cannot tell which objects are “in front of” other objects. You end up with bits of objects that are at relative equivalent depths in the scene, rendering “through” each other. This occurs because the depth resolution is low enough that objects at slightly different depths were assigned the same depth value. The only solution in this case is to increase the depth precision, typically by altering the near plane distances (which give much more bang for your precision buck compared to altering the far clip distances). Google “depth fighting” for more.

    创建视口(viewport)的代码如下:

Viewport *vp = window->addViewport(camera);

vp->setBackgroundColour(ColourValue(0, 0, 0));

colour Value(0,0,0)设定了视口的背景颜色为黑色,它是用RGB值表示的。

    最简单的开始一个渲染循环的方法是从Root调用startRendering()函数:

root->startRendering();

    使用这个方法将使Ogre无止尽地渲染下去直到你手动关闭渲染窗口为止,一个备选的终止渲染的方法是调用Root::getSingleton().queueEndRendering(),但是典型的方法是在你的frame listener返回一个false的值。

    使用frame listener是唯一的在使用startRendering()时调用你自己的代码的途径。创建并且增添一个frame listener到Root的代码如下:

class myFrameListener : public FrameListener {

public:

bool frameStarted (const FrameEvent &evt);

bool frameEnded (const FrameEvent &evt);

};

bool myFrameListener::frameStarted(const FrameEvent &evt) {

//你需要渲染的场景的代码段 really cool stuff to do before a frame is rendered

return true;

}

bool myFrameListener::frameEnded(const FrameEvent &evt) {

//你需要渲染的场景的代码段 really cool stuff to do after a frame is rendered

return true;

}

Root *root = new Root();

MyFrameListener myListener;

// YOU HAVE TO ADD A FRAMELISTENER BEFORE YOU CALL startRendering()!!!

root->addFrameListener(myListener);

root->startRendering();

    你还可以使用frameEnded(),但是这并不常用,使用它你可以在每个帧结束时做些内存清理工作。

Typically during each frame, you process HID (Human Interface Device, such as keyboard, mouse, or joystick) input events. Depending on the particular event, you might cause a model in your scene to move and/or rotate, cause a camera to move or rotate, start a process for a player-character to put a sleeping hex on an NPC troll for 10 exp, or whatever. Regardless of what it is, it occurs during one or both of the FrameListener callback methods—usually during frameStarted(), because more than likely you will process changes to the scene before the scene is rendered, rather than after.

 

OGRE进阶

    Ogre提供了两个函数来手动加载插件,代码如下:

void loadPlugin(const String& pluginName);

void unloadPlugin(const String& pluginName);

    其中pluginName是实际的插件名称,例如“Plugin_ParticleFX”。在提供一个插件的名称时并不要求手动提供扩展,Ogre会自己检测并挂载任何可能的扩展(例如windows下面的.dll动态链接库等)。你当然可以自己实施这些挂载,但是你最好还是让Ogre来做这些,以免发生冲突。

    关于debug与release两个版本的相同的dll文件或者runtime library文件加载错误会导致死机,详情请看:

The “ms_singleton” assert occurs when your application loads a Debug and Release version of the same DLL at execution time. The reason is the nature of the singleton implementation in Ogre. Ogre creates the one-and-only single instance of each of its singleton classes in the Root constructor and access to them is via static member methods. This is all fine and good until your app loads the “opposite” DLL type at runtime, usually because of a plug-in mismatch (Debug plug-ins into a Release app or vice versa). This plug-in will in turn have been linked against the “opposite” OgreMain library, and when they are loaded, the operating system will load that opposite OgreMain library. The next time one of the singleton classes residing in OgreMain is called, the new singleton will try to create an instance of itself, detect that an instance already exists, and BANG! . . . instant assert.

    一般地,为了避免上述情况发生,Ogre自己定义了不同的命名方式,比如在debug版的文件名称上都加上“_d”的后缀,例如“OgreMain_d.dll”。这将要求你使用条件宏来编译程序,代码如下:

Root *root = new Root;

#if defined(_DEBUG)

root->loadPlugin("Plugin_ParticleFX_d.dll");

#else

root->loadPlugin("Plugin_ParticleFX.dll");

#endif

    Ogre主要的几个插件:

Plugin_OctreeSceneManager

Plugin_BSPSceneManager

Plugin_CgProgramManager

Plugin_ParticleFX

RenderSystem_Direct3D9

RenderSystem_GL

    高级的调用Ogre Render的代码:

// create a new Root without config files

Root *root = new Root("", "");

root->loadPlugin("RenderSystem_Direct3D9");

root->loadPlugin("RenderSystem_GL");

    以上代码同时调用了底层的DirectX和OpenGL两个库,由Ogre系统根据实际情况确定使用哪个库。Root系统还提供了一个函数来查看哪个渲染系统被载入并可用:

RenderSystemList* getAvailableRenderers(void);

    以下是Root提供的另外一些获取和操作渲染系统的信息的方法:

void addRenderSystem(RenderSystem* newRend);

RenderSystem* getRenderSystemByName(const String& name);

void setRenderSystem(RenderSystem* system);

RenderSystem* getRenderSystem(void);

    通常你使用setRenderSystem()的时候应该一起调用getAvailableRenderers(),来引导Ogre使用一个可用的底层库,并且通常这是提供给用户的选择。以下是实现这种方式的代码:

// RenderSystemList is a std::vector

RenderSystemList *rlist = root->getAvailableRenderers();

RenderSystemList::iterator it = rList->begin();

while (it != rList->end()) {

// Ogre strings are typedefs of std::string

RenderSystem *rSys = *(it++);

if (rSys->getName().find("OpenGL")) {

//将OpenGL设为可用 set the OpenGL render system for use

root->setRenderSystem(rSys);

break;

}

}

// note that if OpenGL wasn't found, we haven't set a render system yet! This

// will cause an exception in Ogre's startup.

    以上的代码会自动在可用的底层库里搜索OpenGL的名字,并且默认将它设定为底层的支持库。以上代码只是一个示例,实际上你应该使用一个下拉表单列出你搜索到的渲染系统,并把用户所选择的设为Ogre的支撑环境。(原文:Most likely what you will do is populate a drop-down menu in your application’s GUI with the names of the available render systems as provided by getAvailableRenderers(), and then call setRenderSystem() with the user’s selection.)

    手动挂载基于Ogre的应用程序的基本方式如下:

#include "Ogre.h"

// create a new Root without config files

Root *root = new Root("", "");

 

// load the render system plug-ins

root->loadPlugin("RenderSystem_Direct3D9");

root->loadPlugin("RenderSystem_GL");

 

// pretend the user used some other mechanism to select the

// OpenGL renderer

String rName("OpenGL Rendering Subsystem");

RenderSystemList *rList = root->getAvailableRenderers();

RenderSystemList::iterator it = rList->begin();

RenderSystem *rSys = 0;

 

while (it != rList->end()) {

rSys = *(it++);

if (rSys->getName() == rName) {

// set this renderer and break out

root->setRenderSystem(rSys);

break;

}

}

// end gracelessly if the preferred renderer is not available

if (root->getRenderSystem() == NULL) {

delete root;

return –1;

}

 

// We can initialize Root here if we want. "false" tells Root NOT to create

// a render window for us.

root->initialise(false);

 

// set up the render window with all default params

RenderWindow *window = rSys->createRenderWindow(

"Manual Ogre Window", // window name

800, // window width, in pixels

600, // window height, in pixels

false, // fullscreen or not

0); // use defaults for all other values

// from here you can set up your camera and viewports as normal

 

 

 

自己学习OGRE的笔记2

分类: 日记 2007-07-11 22:19

前一段日子忙着准备考试,没怎么学OGRE,只有这一点了,但是现在放假了,不管三七二十一我先逃回家,终于又可以继续学我喜欢的东西了。

 

关于上文中"instance"的解释:

这里解释一下“instance”的涵义,我开始也不理解这个概念,从字面上解释,是实例或场合的意思。实际上,这个概念就像C++编程中类和对象的关系一样。instance是一组可渲染序列的集合。如果你还不理解,看一下一段典型的mental_ray渲染脚本:

verbose on

link "base.so"

$include <base.mi>

options "opt"

    samples -1  2

    contrast 0.1    0.1 0.1

    object space

end options

 

camera "cam"

    frame 1

    output "rgb" "out.rgb"

    focal 50

aperture 44

aspect 1

    resolution 800 800

end camera

 

instance "cam_inst" "cam"

    transform   0.7719  0.3042  -0.5582 0.0

                0.0000  0.8781  0.4785  0.0

                0.6357  -0.3693 0.6778  0.0

                0.0000  0.0000  -2.5000 1.0

end instance

 

light "light1"

    "mib_light_point" (

    "color" 1   1   1,

    "factor"    0.75 )

    origin  0   0   0

end light

 

instance "light1_inst" "light1"

    transform   1   0   0   0

                0   1   0   0

                0   0   1   0

                -2  -3  -2  1

end instance

 

material "mtl"

    opaque

    "mib_illum_phong" (

        "ambient" 0.5   0.5 0.5,

        "diffuse" 0.7   0.7 0.7,

        "ambience" 0.3  0.3 0.3,

        "specular" 1.0  1.0 1.0,

        "exponent" 50,

        "mode" 1,

        "lights" ["light1_inst"]

    )

end material

 

object "cube1"

    visible trace shadow

    tag 1

    group

        -0.5    -0.5    -0.5

        -0.5    -0.5    0.5

        -0.5    0.5     -0.5

        -0.5    0.5     0.5

        0.5     -0.5    -0.5

        0.5     -0.5    0.5

        0.5     0.5     -0.5

        0. 5    0.5     0.5

        v 0     v 1     v 2     v 3

        v 4     v 5     v 6     v 7

        p "mtl" 0   1   3   2

        p       1   5   7   3

        p       5   4   6   7

        p       4   0   2   6

        p       4   5   1   0

        p       2   3   7   6

    end group

end object

 

instance "cube1_inst" "cube1"

    transform   1   0   0   0

                0   1   0   0

                0   0   1   0

                0   0   0   1

end instance

 

instgroup "rootgrp"

    "cam_inst" "light1_inst" "cube1_inst"

    end instgroup

render "rootgrp" "cam_inst" "opt"

可以看出,该脚本也是建立了instance的实例(或者称之为对象)之后来执行渲染的。因此,我们姑且也可以这样来理解instance这个概念,可以说,在OGRE中,场景是以root为实例来建立的,而渲染序列则是以instance为实例来建立的。

 

(接上文代码)

我们不一定要原原本本地做上述代码中的所有的事,我们可以通过其他可编程的代码源来实施创建渲染窗口的调用(render window creation call)。

 

RenderWindow class是源于更普遍适用的类RenderTarget class的,后者是执行渲染面的普遍方式,在没有帧缓冲的渲染目标中很有用,比如说贴图。所有的RenderTarget instance都可以通过它们创建时的名字来存取,上例中creatRenderWindow()的第一个参数(已加粗)就是这个名字。以上API与更多有关的API请看OgreRenderSystem.h文件。

 

如果你想让渲染窗口定位在屏幕的左上角,并且在渲染窗口的标题栏使用一个与渲染窗口不同的名字,以及诸如此类的其他操作,你可以调用NameValuePairList类,该类是STL中map类的一个重定义类型(map是一种映射,更多的请参阅STL库)。

NameValuePairList params;

params["left"] = "0";

params["top"] = 0;

params["title"] = "Alternate Window Title";

RenderWindow *window = rSys->createRenderWindow(

"MainWindow", // RenderTarget name

800, // window width, in pixels

600, // window height, in pixels

false, // windowed mode

&params); // supply the param values set above

以上代码创建了一个名叫“MainWindow”的窗口并让它的标题栏显示为“Alternate Window Title”。

 

你也可以在任何其它窗口系统(例如Qt和wxWidgets)中嵌入Ogre的渲染窗口。RenderWindow中的getCustomAttribute()允许你为渲染窗口获得依赖于系统的窗口控制手柄。你也可以在父窗口中实施Ogre渲染窗口的嵌入,例如:

// hWnd is a handle to an existing Win32 window(详见MSDN)

// renderSystem points to an existing, initialized instance of D3D9RenderSystem

NameValuePairList opts;

opts["parentWindowHandle"] = StringConverter::toString(hWnd);

// everything but "opts" is somewhat irrelevant in the context of an

// explicitly parented window

RenderWindow *window = renderSystem->createRenderWindow(

"WindowName",

800, 600,

false, &opts);

该代码允许你将Ogre渲染窗口嵌入到一个已存在的窗口系统中。但是这样做将使得Ogre窗口的消息迂回传递,你必须管理好Ogre窗口的清理工作。

 

 

 

自己学习OGRE的笔记3

分类: 日记 2007-11-26 12:01

    学习有点紧张, 昨天晚上又看了点书, 把笔记贴出来~~

    另外, 发现OGRE中文终于有起色了, http://ogre3d.cn网站已经完成了1.4.0的官方参考的翻译, 有兴趣的可以去看看. 不过个人觉得还是看APRESS的书比较好, 没有一本入门的书直接看MANUAL和API REF实在太痛苦了~~

 

在你使用一个场景管理器的实例之前,你必须先创建一个:

SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC, "MySceneManager");

在OGRE装载他的插件之前,可供选择的插件有很多种实现,就像前面所说的那样。这些实现的代码会将它们自己注册为特殊的场景管理器:

 

·ST_GENERIC:最小的场景管理器的实现,没有为任何特殊的场景内容和结构作优化。对付简单的场景最合适(比如说那些程序的GUI)。

·ST_INTERIOR:为渲染室内场景、闭合区域场景(close-quarter)、潜在的高密度场景(potentially high-density scene)优化的场景管理器的实现。

·ST_EXTERIOR_CLOSE:为那些渲染部分可见的室外场景优化过的场景管理器实例,例如那些基于单页的多边形地形或者起伏地形。

·ST_EXTERIOR_FAR:OGRE开发中的一个历史性失误,现在一般已经很少用到了。用ST_EXTERIOR_CLOSE或ST_EXTERIOR_REAL_FAR代替。

·ST_EXTERIOR_REAL_FAR:这种场景管理器的实例一般适用于卷动的场景或场景结构。卷动的场景通常都十分巨大,甚至可能是整个未划分的场景。(Scene manager implementation typically suited for paged landscape or paged scene construction. Paged landscapes often are huge, possibly entire planets.)

在前面所说的例子中我们创建了一个ST_GENERIC种类的场景管理器的实例。如果我们在QUAKE 3的级别上使用BSPScenManager(它是被注册为ST_INTERIOR类型的)的插件,我们应该使用ST_INTERIOR场景,并且如果我们想要通过使用TerrainSceneManager(TSM)插件创建一个基于有起伏的地形的场景,我们应该创建一个ST_EXTERIOR_CLOSE的场景管理器。ST_GENERRIC没有提供特殊的插件,但是如果载入了OctreeSceneManager(八叉树),它会接管ST_GENERIC的工作。

 

摄像机

一个摄像机就像真实世界的模拟一样:它从一个特定的方向为你场景的每一帧“照相”(就是说它有一个位置点和一个方向)。它不是一个典型的可渲染的物体,所以即使你在场景中已经有了一个摄像机,这个摄像机也不会被渲染。摄像机既可以被连接到场景的节点上也可以存在于自由的空间中(which means you get to move them around manually if you want their position and/or orientation changed each frame)。就像所提到的那样,摄像机具有拥有一个近切片和一个远切片的视域(field of view)。

我们假设你想要一个具有标准的4:3比例的区域,其近切片距离为5单位,原切片距离为1000单位,摄像机中心轴和上下界的角度皆为30度,下面的代码就创建了这样一个摄像机:

// sceneMgr is an existing instance of a SceneManager implementation. We are

// creating a camera named "MainCam" here.

Camera *camera = sceneMgr->createCamera("MainCam");

// normally you would calculate this from the size of the viewport

camera->setAspectRatio(1.33333f);

// 30 degrees will give more of a long, telescopic view

camera->setFOVy(30.0f);

camera->setNearClipDistance(5.0f);

camera->setFarClipDistance(1000.0f);

 

渲染模式

摄像机可以用三种不同模式中的一种来渲染场景:wireframe(线框图),solid(实体图),或者“points”(只有点被渲染)。

 

camera->setPolygonMode(PM_WIREFRAME);

camera->setPolygonMode(PM_POINTS);

camera->setPolygonMode(PM_SOLID);

PolygonMode mode = camera->getPolygonMode();

 

模式设定在以后的动画中会继续强制执行,直到它被另一个调用改变为止(也就是说这不是一个单帧的设定)。默认的模式是PM_SOLID。

 

位置和变换

一个摄像机是一个可以移动的物体,并享有MovableObject类中的所有的函数和方法。MovableObject最常见的功能是可以连接到场景中的一个节点并且伴随着可渲染的物体移动(“piggyback” the camera along with renderable objects)。你会有需要这么做,比如各种摄像机追踪镜头的技术。你会在后面的章节中看到各种第三人称摄影技术。现在,我们只讨论固定位置和方向的摄像机。

// assume camera is a pointer to an existing, valid instance of "Camera"

camera->setPosition(200, 10, 200);

// you can also use a Vector3 to position the camera, useful for using the

// position obtained from a scene node getter

// camera->setPosition(Vector3(200, 10, 200));

这段代码会把摄像机放置在绝对坐标(200,10,200)处。这和move()与moveRelative()方法是不同的,后面所说的这些方法分别是在世界坐标和本地坐标中针对当前位置移动物体。

// camera is at world space 200, 10, 200 from before

camera->move(10, 0, 0); // camera moves to 210, 10, 200

camera->moveRelative(0, 0, 10); // camera moves to 210, 10, 210

我们必须注意moveRelative()。因为它发生在本地坐标系中,这种移动的实施和摄像机当时的方向有关。在前面所述的例子中,我们假定摄像机是和主轴线对齐的,指向正Z轴方向。如果摄像机向右转了90度,例如,moveRelative(0,0,10)会把摄像机移到(220,10,200)的地方。

 

方向、指向和“面向”(Direction, Orientation, and ”Look-At”)

OGRE提供了一系列丰富的方法来放置你的摄像机:

 

void setDirection(Real x, Real y, Real z);

void setDirection(const Vector3& vec);

Vector3 getDirection(void) const;

Vector3 getUp(void) const;

Vector3 getRight(void) const;

void lookAt( const Vector3& targetPoint );

void lookAt(Real x, Real y, Real z);

void roll(const Radian& angle);

void roll(Real degrees) { roll ( Angle(degrees) ); }

void yaw(const Radian& angle);

void yaw(Real degrees) { yaw ( Angle(degrees) ); }

void pitch(const Radian& angle);

void pitch(Real degrees) { pitch ( Angle(degrees) ); }

void rotate(const Vector3& axis, const Radian& angle);

void rotate(const Vector3& axis, Real degrees) {

rotate ( axis, Angle(degrees) ); }

void rotate(const Quaternion& q);

void setFixedYawAxis( bool useFixed, const Vector3& fixedAxis =

const Quaternion& getOrientation(void) const;

void setOrientation(const Quaternion& q);

void setAutoTracking(bool enabled, SceneNode* target = 0,

const Vector3& offset = Vector3::ZERO);

 

这些方法的功能大多数从名字来看是显而易见(self-explanatory)的。roll(), yaw(), 和pitch()做的正好就是他们所描述的。setDirection()会让摄像机指向该向量定义的方向。rotate()会使摄像机沿着给定的坐标轴旋转一个角度。lookAt()是非常常用的,它可以使一个摄像机指向一个目标点或目标物体的中心,并且不需要用欧氏几何的方法去计算该指向的四元矩阵。最后,setFixedYawAxis()允许你打破摄像机自有的偏移轴的约束(break the camera free from its own yaw (Y) axis)。在第一人称射击游戏中,摄像机通常能够检视X-Z平面。正因如此,你希望的默认动作,是沿着摄像机本地坐标的Y轴偏移。但是,在飞行模拟游戏中,你希望能打破这种约束来创造一个完全自由的摄像机。

setAutoTracking()是一个有趣的功能,如果你希望摄像机总是沿着场景中特定的一点。注意这和真正的第三人称视点的摄像机不同,因为那种摄像机通常不是面向一个特定点的,而是总是面向你的角色所看的方向的。第一个参数指出是否采用追踪,这在任何一帧开始渲染前都可以做,并且在被追踪前必须先删除这个节点。被追踪的节点是第二个参数,这个节点必须存在于这个方法被调用之前。这个参数只能在第一个参数是false的情况下定为NULL。如果被追踪的物体是巨大的并且不适合面向正中间来观察,你可以用第三个参数(偏移offset)来协调一下实际的视点,该参数在本地坐标中操作被追踪的节点。

以下的方法可以用来获得摄像机实际方向的信息,注意把旋转操作和平移操作对被连接的节点的影响考虑在内:

 

const Quaternion& getDerivedOrientation(void) const;

const Vector3& getDerivedPosition(void) const;

Vector3 getDerivedDirection(void) const;

Vector3 getDerivedUp(void) const;

Vector3 getDerivedRight(void) const;

const Quaternion& getRealOrientation(void) const;

const Vector3& getRealPosition(void) const;

Vector3 getRealDirection(void) const;

Vector3 getRealUp(void) const;

Vector3 getRealRight(void) const;

 

Real的方法返回的是世界坐标中的值,但是Derived返回的是在本地坐标中的值。

 

进阶摄像机功能

OGRE通过setFrustumOffset()和setFocalLength()方法支持真实的渲染。例如,你可以通过水平地适配一个摄像机来模拟眼睛间的距离。这会在适配的摄像机中渲染出一个有微小角度区别的画面,以此产生真实的输出。当然,这种技术是高度专业化和细节很多的,但是它确被提供给这种需要的人。

OGRE也允许你直接操作视点和法线的矩阵。这当然是更高级的话题了,回到我们先前所说的“只有在你确信知道你所做并且为何这么做的时候才做”上来,因为这些矩阵在你设定摄像机的时候就已经为你计算好了。下面的方法是用来操作view/projection的矩阵的:

const Matrix4& getProjectionMatrixRS(void) const;

const Matrix4& getProjectionMatrixWithRSDepth(void) const;

const Matrix4& getProjectionMatrix(void) const;

const Matrix4& getViewMatrix(void) const;

void setCustomViewMatrix(bool enable,

const Matrix4& viewMatrix = Matrix4::IDENTITY);

bool isCustomViewMatrixEnabled(void) const;

void setCustomProjectionMatrix(bool enable,

const Matrix4& projectionMatrix = Matrix4::IDENTITY);

bool isCustomProjectionMatrixEnabled(void) const;

 

列出的前两行代码返回渲染系统设定的法线矩阵。getProjectionMatrixRS()会返回渲染系统得本地坐标,而getProjectionMatrixWithRSDepth()会以OGRE本身的格式(右手坐标系的方式)返回矩阵。区别是深度值(depth value)会根据所用的渲染系统而覆盖[0,1]或[-1,1]的范围。你可以用调用getProjectionMatrix()来避免这些使其总是返回[-1,1]间的一个值。

 

 

 

自己学习OGRE的笔记4

分类: 日记 2007-11-26 21:33

    今天又看了点,逮到时间上网就再发点上来吧.

  

当你设定了一个自定义的view或者projection矩阵,你必须意识到OGRE不再根据摄像机的移动或转向来更新矩阵数据。你必须在每次你移动它的数据源的时候手动更新这个矩阵。你可以用enable参数在setCustomViewMatrix()和setCustomProjectionMatrix()中开关自定义矩阵功能。当这个功能被关闭时,OGRE会像平常一样为你继续更新内部矩阵。

 

大多数时候,OGRE的LOD(level of detail)手柄控制得相当完美。可是,有些时候你需要越过它的默认设置,OGRE提供了这样的Camera方法来做这些:

void setLodBias(Real factor = 1.0);

Real getLodBias(void) const;

setLodBias()其实不是一个固定的指令。场景中的元素可以忽略这条指令,making it more of a hint if LoD overrides are widespread in the scene or application。factor参数控制摄像机增减它渲染显示的细节。当其大于1.0时增加渲染细节,小于1.0时减少渲染细节。这在实施不需要全部细节的摄像机例如rear-view(这里可能是打印错误,不知道rear-view是什么东西)时很有用。

 

将全部场景的部分显示到屏幕上

一个非常平常的需要,尤其在大量使用鼠标操作的程序中,就是将全部场景的部分按一定方式显示到屏幕上。摄像机的中心轴线和屏幕相交的点是一个特殊的点,也就是[0.5,0.5]那一点(在标准坐标系中)。如果你在屏幕上移动鼠标指针,你可能想要以摄像机为原点到鼠标所在位置定义一条线。这条线就是一条射线(ray),它在getCameraToViewportRay()函数中返回:

// x and y are in “normalized” (0.0 to 1.0) screen coordinates

Ray getCameraToViewportRay(Real x, Real y) const;

通过这条线,你可以通过场景管理器查询哪些物体和这条线相交。关于场景管理器的更详细的细节将在这本书的后面介绍。

 

视口(Viewport)

一个Viewport的实例通过一个RenderWindow的实例获得,这种检索视口的方法使用Camera的实例作为长参数(and the method that retrieves the viewport takes an instance of Camera as the long parameter, 没怎么看明白)。这意味着单个的Camera实例可以驱动0个以上的Viewport对象。

视口不使用ill效果就可以重叠。默认的,OGRE在渲染后一个视口中的物体时会对前一个视口的depth和color数据缓冲先进行清理,来避免depth-blending(指不同视口的不同深度数据的混淆)的错误。如果需要你可以有无数个摄像机和视口。使用多重摄像机和视口的一个通常的作用是在游戏中层叠缩放的窗口(“picture-in-picture” zoom windows in a game)。

 

Tip Something to keep in mind when considering viewports and camera zoom is that it is not enough simply to narrow the field of view on the camera to create a zoomed image. This is because Ogre will still be using the camera position to calculate level of detail for the zoom, and what you will find is that the objects in your zoom window will look terrible, since they will still be at the correct level of detail for their distance from the camera. Solution: when you zoom, move the camera closer to the zoom target, or use a secondary camera (which is required for picture-in-picture zooms), or use Camera::setLodBias() to increase the level of detail rendered.

 

大意是,考虑摄像机视口的缩放时不仅仅是缩放视口数据,OGRE使用摄像机位置来计算缩放所显示的细节,你在缩放后的窗口中看到的物体会严重变形,因为它们还在原先的距离上的level of detail上。解决方法是:当你进行缩放的时候,将摄像机移近被缩放的目标,或者使用另一个摄像机,或者使用Camera::setLodBias()来增加渲染细节。

 

视口有一个z-order,这个参数用来指出哪个视口的渲染结果会被另一个视口用到。高优先级的视口会被放到栈的上部。只有viewport可以占据一个给出的渲染窗口的z-order。例如,如果你有两个z-order 0上的视口,将会抛出一个异常。

每个视口可以有一个独立的背景颜色。例如,你可以有一个在z-order 1下的覆盖整个窗口的主视口,它的背景色为黑色,和一个小一点的z-order 0上的视口,蓝色背景,如以下代码所示:

// assume window is a valid pointer to an existing render window, and camera is

// a valid pointer to an existing camera instance

Viewport *vpTop, *vpBottom;

// second parameter is z-order, remaining params are position and size, respectively

vpBottom = window->addViewport(camera, 0);

// create a smaller viewport on top, in the center, 25% of main vp size

vpTop = window->addViewport(camera, 1,

0.375f, 0.375f,

0.25, 0.25);

// set the background of the top window to blue (the default is black so we don't

// need to set the bottom window explicitly)

vpTop->setBackgroundColour(ColourValue(0.0f, 0.0f, 1.0f));

// an alternate way to set the color is to use the manifest constant for blue

// vpTop->setBackgroundColour(ColourValue::Blue);

 

我前面提到OGRE默认在每帧开始前清理depth和color数据缓冲。你可以独立操作这些设定,使用Viewport中的setClearEveryFrame()函数:

// As mentioned, these both default to “true”. The flags are maskable; in other

// words, setClearEveryFrame(true, FBT_COLOUR|FBT_DEPTH) is valid.

vpTop->setClearEveryFrame(true, FBT_COLOUR);

vpTop->setClearEveryFrame(false);

 

使用picture-in-picture式的视口时另一个需要考虑到的因素就是overlays覆盖物,这些overlays默认情况下在任何视口中都是可见的。这不是你在一个缩放窗口中想要的,所以你可以关闭overlay渲染。同样skyboxes和shadows也一样。

vpTop->setOverlaysEnabled(false);

vpTop->setSkiesEnabled(false);

vpTop->setShadowEnabled(true);

 

你可以做更多更先进的操作,例如渲染序列并且使用一个per-viewport材质结构,本书以后会讲到这些。

 

主渲染循环(main rendering loop)

典型的OGRE程序会按帧不间断地依次渲染。我们先前就知道调用这个渲染循环的方法:使用Root::startRendering()方法。但是,这种方法只是简单地开始一个小的渲染循环和调用另一个函数:renderOneFrame()。

renderOneFrame()存在的理由,首先,你可能希望将ogre合并进一个程序或者模版中。其次,你可能需要重定义渲染方式。

下面所示的代码被故意写到最简,它旨在向你演示你应该怎样在你的程序中手动设置渲染循环:

bool keepRendering = true;

 

// Do all of the Ogre setup we've covered so far in this chapter: loading plug-ins,

// creating render window and scene manager and camera and viewport, and putting

// some stuff in our scene.

 

while (keepRendering)

{

// process some network events into engine messages

// process some input events into engine messages

// update scene graph (manager) based on new messages

// render the next frame based on the new scene manager state

root->renderOneFrame();

 

// check to see if we should stop rendering

// Note: NextMessageInQueue() is completely fictional and used here only

// for purposes of illustration -- it does not exist in Ogre.

 

if (NextMessageInQueue() == QUIT)

{

keepRendering = false;

}

}

 

// Do whatever cleanup your application needs

// Then, shut down Ogre

delete root;

 

不重要的,看renderOneFrame()函数的代码:

Bool Root::renderOneFrame(void)

{

    If(!_fireFrameStarted())

        Return false;

 

    _updateAllRenderTargets();

    Return _fireFrameEnded();

}

 

 

总结

在这一章中,学习了怎样初始化和设定Ogre程序,和各种可见的类(RenderWindow, Camera, Viewport)是怎样配合工作和显示你的场景的。在下一章中,我们将开始讲解真正的内容。

 

 

这个星球上(可能在其它星球上也是)每个3D渲染库都使用一个场景图来组织它可渲染的项目。(原句:Every 3D rendering library on the planet (and even some on other planets) uses a scene graph to organize its renderable items. 搞笑~~) 这个scene graph通常都是为了更快地搜索和查询而优化过的。有时scene graph也被用作碰撞检测(collision detection,一种在场景中求交的运算)。有时候单个的scene graph也被用在程序中任何一个子系统上,包括音效和物理模拟系统。

 

在OGRE中你可以在同一场景中同时使用多个场景管理器。

 

一般来说,OGRE中的场景管理器有以下作用:

·在场景中创建和放置可移动的物体、光源,和摄像机,并且有效地将它们注册到一个穿程图中(access them efficiently in a graph traversal)。

·载入和装配世界坐标中的几何体(differentiated from movable entities in that world geometry is large, sprawling, and generally not movable)

·执行场景序列并且提供这样问题的答案:哪些物体包含在以世界坐标系中某一点为中心的圆中?

·拣选出不可见的物体和将可见的物体加入渲染序列中。

·从当前渲染视图的透视图中依增加的距离组织和分类无向的光线。(Organizing and sorting (by increasing distance) nondirectional lights from the perspective of the current renderable)

·设定和渲染场景中的阴影。

·设定和渲染场景中的所有其它物体,比如说背景和Skyboxex

·将这种组织后的目录传递给渲染系统

 

 

 

自己学习OGRE的笔记5(值得好好看看)

分类: 日记 2007-12-05 22:26

现在网上已经可以找到PRO OGRE 3D PROGRAMMING这本书的中文译本, 翻得还是挺不错的, 至少比直接看英文版舒服多了(ORZ....) 不管怎么说, 看中文版的进度快多了, 有些地方看不太懂的可以回英文的原书看一下. 总的来说, 没有STD的基础学OGRE还是很痛苦的, 还有, 想用OGRE写个象样的程序的话, 会点计算机图形学的基础还是很必要的, 要是写的程序用到的3D场景只是象仙剑3那个级别的话, 还是自己写个引擎好了, terrain的场景管理器对那样平面的地形实在是太浪费了, 你甚至用不上八叉树那种分割方式, 按我原先想的甚至只要简单的二维映射, 一句话, OGRE太复杂了, 很多东西理解不了, 尤其是低层优化时要用到的cg语言, 当真会了这些, 你已经完全可以自己写一个引擎了.

另外感叹一句, 那个传说中的CELayoutEditor的bug多得真是吓人啊~~~~~~而且CEGUI官方完全没有补正它的意思, 想找捷径还是不可取的, 自己动手, 丰衣足食.

我想加快时间拿个DEMO出来, 至于程序的其他部分以后再说吧...

 

场景管理器的种类

OGRE论坛上经常会有人这样提问:“为什么你们要强调‘场景种类’呢?为什么不直接把它们交给场景管理器呢?”原因是场景管理器是OGRE当成插件载入的,你可以为你的程序载入多个不同的场景管理器插件。

 

场景中物体的创建

场景管理器最平常的作用就是以各种方式创建物体,不管是可移动的还是不可移动的:光源,摄像机,实体,粒子系统,公告牌,等等,以及skybox,静态几何体,和载入地图。所有的存在于一个场景中的物体都被场景管理器管理。这就允许开发者在需要时自定义物体创建的方式及程序的行为。注意管理器管理所有被其创建的物体的生命周期:每种物体都提供了创建、选取、删除和删除所有的方法。所有由场景管理器创建的物体必须由相应的场景管理器来删除:换言之,退出了场景管理器猴你不能删除任何该场景中的指针。

 

Caution If you destroy a scene node with live (meaning referenced) content or data still attached, Ogre will not crash, but you will need to ensure that the objects that were attached to the node are properly disposed of to avoid memory leaks. You can manage the lifetime of scene nodes as you see fit, but you do have to exercise caution and due diligence with your godlike powers over the helpless scene nodes.

注意:如果你删除一个正在被引用的节点,OGRE不会崩溃,但是你将需要确认连接到该节点的物体不会导致内存溢出。你可以按你认为合适的方式控制场景节点的生命周期,但是你必须以造物主般的负责精神来管理这些任你摆弄的场景节点。

 

了解三个主要的空间变换(移动、旋转、缩放)是通过场景节点来实施的,而不是具体的物体本身。换言之,是场景的节点在移动,而不是场景中的物体;场景中的物体只是个驾驭者。

 

场景查询

场景管理器的第二大用处就是场景查询:ray queries, sphere(区域) queries, bounding-box(边界盒) queries, bounding-plane queries, and intersection(碰撞) queries.

这些查询结果是实时更新的,是为诸如地形检测(terrain-clamping)之类的情形服务的。

 

Note Terrain clamping refers to the process of maintaining a constant distance (possibly zero) between an object and world geometry (usually outdoor terrain or landscapes). In order to do this, you need to know the height of the terrain directly underneath an object, and the simplest way to do that in Ogre is with a raycast (指直线的正投射) (ray scene query) pointing straight down, to obtain a world fragment. You can then get the world height of that fragment and set your object’s offset on that basis. This is a very common technique.

 

空间关系和三维转换 (Spatial Relationships and 3D Transforms)

那些初接触3D软件开发的人通常感到很难区分节点和content objects的关系,甚至是节点与节点间的关系。为了是他们易于理解,让我们看些实际的三维软件的例子吧。

 

空间关系

当你回忆那些基本的三维算法的时候,你知道position(位置坐标), orientation(方向), 和scale(缩放比例)可以表达成一个特殊关系空间中的4×4的矩阵。这个关系空间被定义为 “一系列给定的坐标轴(in terms of a given set of coordinate axes)”。

 

静态几何体(Static Geometry)

看起来似乎静态几何体是活动物体(Moveable Object)的反义词,但事实上也不全是:通常来说静态几何体会由很多不再活动的活动物体来构成。

 

在这里需要要再次提到这个问题,现代的GPU更适合渲染少量巨大物体,而不是很多小几何片断。

 

注意:因为这个原因,当在现在图形硬件上来测试3D应用程序性能时,光衡量渲染三角面数量的能力通常是没有意义的事情:比如当一百万个三角面捆绑成一个簇里能达到每秒300帧的渲染速度,在结构组织改变的时候,把这些三角形分布在一千个簇中(平均每簇一千个三角面),就可能会降低到30帧每秒这样的结果。所以在现代3D应用程序中可渲染三角面的数量已经不是衡量应用程序的唯一标准。

 

所以通常而言,越多的三角面集中在一个簇中渲染,对提升你的应用程序效率越有利(当然,也不能盲目,很多时候还要考虑诸如图形硬件带宽吞吐量等诸多因素的影响。)

 

可能在这里你会觉得把复杂的场景作为静态物体来处理可能是一个不错的注意,但事实也并非如此,在下面列出静态物体的几个缺点:

 

·一般而言,巨大的静态物体需要在使用前被构建,通常这是一个缓慢的过程,所以不能在每一帧都执行。

 

· 静态几何体将按着放入物体的材质来进行分类,并且把材质相同的集合体放置到同一个渲染设置当中(换句话说,也就是放到同一个簇中)。但这并不表示只要把一组几何体打包成一个静态物体就能神奇的把它们捆绑到一个渲染设置中:不同材质的物体仍然会被拆分成不同的簇中被渲染。所以这时候你需要在两种需求中进行折衷:是在一次调用中渲染最多的三角形还是最少的调用的次数(也就意味着决定在静态物体中放置多少不同的材质)。

 

·在静态几何体中“静态”的含义是:一旦物体被放入静态几何体中,你就不能在单独移动它了。任何对静态几何体的世界变换都会应用到所有里面包含的物体上。这就好像放入咖啡中的方糖,你可以扔进去,但是再也捞不到它了。

 

·通常来说静态几何体会比同样大小的活动物体占用更多的内存;这是因为对于活动物体来说,可以多个实体共享一个网格模型。而静态几何体会为每一个实体创建一个网格模型数据的拷贝。不过在这本书写作的时候,GPU的几何处理也正在发生变革,可能在这本书出版之后,硬件会支持更加优化静态几何体的处理方式。

 

·就算在你的视野里(视截体)中看到了整个静态几何体的一小部分,甚至包括在你身后的整个数据都会传到图形硬件中渲染。假如你把场景中所有的植被都放到一个静态物体中,即使你只看到一颗小草,那么整个森林都会被渲染。

此外,在场景对静态几何体材质和模型的LoD(细节等级)处理中,会遵照整个静态场景最远的物体距离来设定整组物体的等级。这导致当距离改变的时候,整个静态物体的LoD都会有相同的改变。不过除了上面说的问题之外也有好的一面,场景管理器会通过空间分割算法把静态几何体分组,进而把那些没有在显示空间的静态几何体直接屏蔽到渲染队列之外。对于静态几何体的处理就和所有软件工程中所出现的问题一样,你必须为提高程序的性能而进行一系列设计上的折衷。

 

 

具体例子见 第五章-〉通过例子了解场景管理器。。

virtual void chooseSceneManager(void) // 为什么这里是virtual??

    {

        // Create the SceneManager, in this case a generic one

        mSceneMgr = mRoot->createSceneManager(ST_GENERIC, "ExampleSMInstance");

    }

    virtual void createCamera(void)

    {

        // Create the camera 看下代码中createCamera的返回值是什么型的!!

        mCamera = mSceneMgr->createCamera("PlayerCam");

 

        // Position it at 500 in Z direction

        mCamera->setPosition(Vector3(0,0,500));

        // Look back along –Z lookAt函数见前面所述

        mCamera->lookAt(Vector3(0,0,-300));

        mCamera->setNearClipDistance(5);

        mCamera->setFarClipDistance(1000);

// 原书的说明:在代码中,视截体的近截面被设置为5个世界单位;在本书前面提到过,在默认的情况下,Ogre把近截面的距离设置为100单位,远截面为1000 000单位。但是事实上这并不是一个好主意,为了确保深度缓存精度的细致,把远近界面调整为近似1000:1的比率会得到较好的效果。所以在代码5-1中的最后,我擅自增加了一行演示程序中没有的代码,把远截面的距离调整为1000(而不是默认的1000 000)。

    }

 

void createScene(void)

{

   // Set ambient light

   mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

   // Create a point light

   Light* l = mSceneMgr->createLight(“MainLight”);

  

   // Accept default settings: point light, white diffuse, just set position

   // NB I could attach the light to a SceneNode if I wanted it to move

   // automatically with other objects, but I don’t

   l->set Position(20,80,50);

  

   Entity * ent = mSceneMgr->createEntity(“head”, “ogrehead.mesh”);

 

   // Set material loaded from Example.material

   Ent->setMaterialName(“Examples/EnvMappedRustySteel”);

 

   // Add entity to the root scene node

   mSceneMgr->gerRootSceneNode()->createChildSceneNode()->attachObject(ent);

}

 

场景节点在默认的情况下变换空间为父节点空间(TS_PARENT),所以在使用父节点空间变换的时候不需要额外的设置:

 

mSceneNode->translate(100.0, 10.0, 0.0);

 

但是如果需要在世界空间进行节点变换,就的通过下面的方法来调用:

 

mSceneNode->translate(100.0, 10.0, 0.0, TS_WORLD);

 

对于本体空间的变换也同样如此。下面表示将一个节点沿着它朝向的方向“向前”移动100个单位:

 

mSceneNode->translate(0.0,0.0,100.0,TS_LOCAL);

 

对于旋转场景节点而言,默认的变换空间为本地空间(TS_LOCAL);如果你需要不同的旋转空间,必须明确的告知Ogre:

 

// 对象绕自己的Y轴旋转一弧度,大约57角度

mSceneNode->yaw(Ogre::Radian(1.0));

 

// 对象绕父节点的X轴旋转一弧度,大约57角度

mSceneNode->pitch(Ogre::Radian(1.0), TS_PARENT);

 

// 对象绕世界的Z轴旋转一弧度,大约57角度

mSceneNode->roll(Ogre::Radian(1.0),TS_WORLD);

 

缩放不需要关系空间;它在节点本身执行,同时影响节点的所有子节点。

 

// 在X轴缩放两倍,其他轴不缩放

mSceneNode->scale(2.0, 1.0, 1.0);

 

 

提示:如果你在缩放场景节点的时候,发现实体模型忽然产生了奇怪的变化。可能是因为法线也一同被缩放所致,因为光线计算会参照标准法线的数据,而缩放后的法线将会对计算产生相应的影响。解决的办法是通过在缩放后调用setNormaliseNormals()方法,不过这也会消耗一些执行效率。

 

载入世界地图

至今为止的演示都是载入对象到一个空的世界。大多数应用程序,需要有一个“布景”来作为放置对象的场所:换句话说就是载入“世界地图”。Ogre的场景管理器提供一个可以方便的用来载入世界地图到场景中的方法。

 

基本室外场景

在这里首先让我们看一下Terrain演示程序的代码

    void createScene(void)

{

        // Set ambient light

        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

        // Create a light

        Light* l = mSceneMgr->createLight("MainLight");

       

// Accept default settings: point light, white diffuse, just set position

        l->setPosition(20,80,50);

 

        ColourValue fadeColour(0.93, 0.86, 0.76);

        mSceneMgr->setFog( FOG_LINEAR, fadeColour, .001, 500, 1000);

        mWindow->getViewport(0)->setBackgroundColour(fadeColour);

 

        std::string terrain_cfg(“terrain.cfg”);

        mSceneMgr -> setWorldGeometry(terrain_cfg);

 

        // Infinite far plane?

        if (mRoot->getRenderSystem()->getCapabilities()->hasCapability(RSC_INFINITE_FAR_PLANE))

        {

            mCamera->setFarClipDistance(0);

       

        // Define the required skyplane

        Plane plane;

        // 5000 world units from the camera

        plane.d = 5000;

        // Above the camera, facing down

        plane.normal = -Vector3::UNIT_Y;

 

        // Set a nice viewpoint

        mCamera->setPosition(707,2500,528);

        mCamera->lookAt(0, 0, 0);

}

 

下面是OGRE.CFG:

# The main world texture (if you wish the terrain manager to create a material for you)

WorldTexture=terrain_texture.jpg

 

# The detail texture (if you wish the terrain manager to create a material for you)

DetailTexture=terrain_detail.jpg

 

#number of times the detail texture will tile in a terrain tile

DetailTile=3   // 这里又是什么??

 

# Heightmap source

PageSource=Heightmap

 

# Heightmap-source specific settings

Heightmap.image=terrain.png

 

# If you use RAW, fill in the below too

# RAW-specific setting - size (horizontal/vertical)

#Heightmap.raw.size=513

# RAW-specific setting - bytes per pixel (1 = 8bit, 2=16bit)

#Heightmap.raw.bpp=2

 

# How large is a page of tiles (in vertices)? Must be (2^n)+1

PageSize=513   // 是什么??

 

# How large is each tile? Must be (2^n)+1 and be smaller than PageSize

TileSize=65

 

# The maximum error allowed when determining which LOD to use

MaxPixelError=3

 

# The size of a terrain page, in world units

PageWorldX=1500

PageWorldZ=1500

# Maximum height of the terrain

MaxHeight=100

 

# Upper LOD limit

MaxMipMapLevel=5

 

#VertexNormals=yes

#VertexColors=yes

#UseTriStrips=yes

 

# Use vertex program to morph LODs, if available

VertexProgramMorph=yes

 

# The proportional distance range at which the LOD morph starts to take effect

# This is as a proportion of the distance between the current LODs effective range,

# and the effective range of the next lower LOD

LODMorphStart=0.2

 

# This following section is for if you want to provide your own terrain shading routine

# Note that since you define your textures within the material this makes the

# WorldTexture and DetailTexture settings redundant

 

# The name of the vertex program parameter you wish to bind the morph LOD factor to

# this is 0 when there is no adjustment (highest) to 1 when the morph takes it completely

# to the same position as the next lower LOD

# USE THIS IF YOU USE HIGH-LEVEL VERTEX PROGRAMS WITH LOD MORPHING

#MorphLODFactorParamName=morphFactor

 

# The index of the vertex program parameter you wish to bind the morph LOD factor to

# this is 0 when there is no adjustment (highest) to 1 when the morph takes it completely

# to the same position as the next lower LOD

# USE THIS IF YOU USE ASSEMBLER VERTEX PROGRAMS WITH LOD MORPHING

#MorphLODFactorParamIndex=4

 

# The name of the material you will define to shade the terrain

#CustomMaterialName=TestTerrainMaterial

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值