第九章 平面
运行截图:
前几节悬空的模型没有为我们提供一个“踏实”的参照系,难以体现物体空间位置的变化,因为没有地面。所以在学习移动、缩放和旋转之前,我们先学习创造一个地面。
要在场景中创建并渲染一个平面,需要下面三个步骤: 1. 定义平面; 2. 从定义的平面创建平面模型; 3. 将平面模型绑定到场景节点。
Plane plane; // 定义平面
plane.normal = Vector3::UNIT_Y; // 定义平面的法线方向(也就是平面正面的朝向)
plane.d = 100; // 定义平面与世界原点的距离
平面 ( Plane ) 是 OGRE 唯一的一种内置简单几何体 (Primitive) 。当然,如果你高兴,你可以修改 OGRE 的内核代码,使它能够创建更多类型的简单几何体例如立方体 (Cube/Box) 、球体 (Sphere) 、柱体 (Cylinder) 、锥体 (Cone) 、 圆环体 (Torus) 、或者茶壶 (Teapot) ,很多商业引擎具有直接创建这些简单几何体的能力,但是 OGRE 目前只能创建简单平面。
OGRE:: Plane (平面) 对象的定义参看头文件 OgrePlane.h ,它有五个重载的构造函数,意味着我们可以使用五种不同的方法来创建一个平面:
// 使用默认设置直接创建平面
Plane::Plane ()
{
normal = Vector3::ZERO;
d = 0.0;
}
// 使用现有平面的设置新建一个平面
Plane::Plane (const Plane& rhs)
{
normal = rhs.normal;
d = rhs.d;
}
// 由法线方向和平面到世界原点的距离创建一个平面
Plane::Plane (const Vector3& rkNormal, Real fConstant)
{
normal = rkNormal;
d = -fConstant;
}
// 由法线方向和平面上的一点创建一个平面
Plane::Plane (const Vector3& rkNormal, const Vector3& rkPoint)
{
normal = rkNormal;
d = -rkNormal.dotProduct(rkPoint);
}
// 三点确定一个平面
Plane::Plane (const Vector3& rkPoint0, const Vector3& rkPoint1, const Vector3& rkPoint2)
{
redefine(rkPoint0, rkPoint1, rkPoint2);
}
平面具有两种基本属性:
Vector3 normal; // 法线方向
Real d; // 与世界原点的距离
Mesh* pGround = MeshManager::getSingleton().createPlane(
"GroundPlane", // 模型名称
plane, // 平面定义
2000, // X 方向宽度
1000, // Z 方向宽度
10, // X 方向分割
5, // Z 方向分割
true, // 是否创建法线
2, // 纹理坐标数量
16, // U 方向纹理铺嵌的行数
8, // V 方向纹理铺嵌的行数
Vector3::UNIT_Z // 正面朝向
);
在第一步中,我们仅仅是定义了一个非常抽象的平面,这个抽象的平面只拥有法线方向和与世界原点的距离两个属性,在第二步,我们就要使用 createPlane 方法使这个平面具体化,赋予它名称、尺寸、分割、纹理坐标等具体属性,正式构建出一个平面模型 ( Mesh ) 。
MeshManager:: createPlane () (创建平面)方法在头文件 OgreMeshManager.h 中定义:
Mesh * Ogre::MeshManager::createPlane (
const String & name, // 平面模型的名称
const Plane & plane, // 所使用的平面定义的名称
Real width, // 平面宽度 (X 方向)
Real height, // 平面高度 (Y 方向)
int xsegments = 1, // X 方向分割数目
int ysegments = 1, // Y 方向分割数目
bool normals = true, // 是否创建垂直于平面的法线
int numTexCoordSets = 1, // 纹理坐标集的数目(也就是多层纹理的层数)
Real uTile = 1.0f, // U 方向纹理铺嵌行数
Real vTile = 1.0f, // V 方向纹理铺嵌行数
const Vector3 & upVector = Vector3::UNIT_Y,
// 上方向法线,指示平面的正面朝向
HardwareBuffer::Usage vertexBufferUsage = HardwareBuffer::HBU_STATIC_WRITE_ONLY,
// 顶点缓存用途
HardwareBuffer::Usage indexBufferUsage = HardwareBuffer::HBU_STATIC_WRITE_ONLY,
// 索引缓存用途
bool vertexShadowBuffer = true, // 顶点阴影缓存
bool indexShadowBuffer = true // 索引阴影缓存
)
后面四个参数目前暂不深究,使用默认值。
// 创建实体(地面)
Entity* entGround = mSceneMgr->createEntity( "ground", "GroundPlane" );
// 为地面设置材质
entGround->setMaterialName( "Examples/Rockwall" );
// 在场景根节点下创建一个子节点用于绑定这个地面实体
SceneNode* groundNode = rootNode->createChildSceneNode();
// 把地面实体绑定到这个子节点
groundNode->attachObject( entGround );
Entity:: setMaterialName () (设置材质名称) 方法在头文件 OgreEntity.h 中定义,使用这个方法可以整体替换模型的本体材质,非常方便:
void Ogre::Entity:: setMaterialName ( const String & name )
材质和模型、粒子一样,属于一种预制资源。打开 OGRE\Samples\Media\materials\scripts\ 目录,可以在下面看到一系列后缀是 *.material 的文件,这些都是材质的定义脚本。使用文本编辑器打开 Example.material ,在里面你可以查找到包含 Examples/Rockwall 字样的脚本段落,它在这个文件的最后:
material Examples/Rockwall // material 材质名称
{
technique // 材质渲染技术块
{
pass // 材质渲染通道
{
texture_unit // 纹理单位
{
texture rockwall.tga // texture 纹理贴图名称
}
}
}
}
其中的纹理贴图 rockwall.tga ,和其它所有的贴图文件一起,位于 OGRE\Samples\Media\textures\ 目录下,在这里我们不用指定贴图的路径, 凭借配置文件 resources.cfg , OGRE 可以自动找到这张贴图。
材质脚本的详细内容以后找时间再介绍。
最后,我用离线浏览工具下载了 OGRE 官网的手册 (Manual) ,里面有关于材质脚本的解说,比较详细,可以先看看。