转:Ogre TerrainGroup地形赏析

2023-11-09

转:Ogre TerrainGroup地形赏析

1.1  参考

http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Ogre+Terrain+System

http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Ogre+Terrain+Component+FAQ

 

New Terrain Early Shots

http://www.ogre3d.org/forums/viewtopic.php?f=11&t=50674

 

http://tulrich.com/geekstuff/sig-notes.pdf

 

ogre_src_v1-8-1\Components\Terrain

├─include

│      OgreTerrain.h

│      OgreTerrainGroup.h

│      OgreTerrainLayerBlendMap.h

│      OgreTerrainMaterialGenerator.h

│      OgreTerrainMaterialGeneratorA.h

│      OgreTerrainPagedWorldSection.h

│      OgreTerrainPaging.h

│      OgreTerrainPrerequisites.h

│      OgreTerrainQuadTreeNode.h

│     

└─src

        OgreTerrain.cpp

        OgreTerrainGroup.cpp

        OgreTerrainLayerBlendMap.cpp

        OgreTerrainMaterialGenerator.cpp

        OgreTerrainMaterialGeneratorA.cpp

        OgreTerrainPagedWorldSection.cpp

        OgreTerrainPaging.cpp

        OgreTerrainQuadTreeNode.cpp

 

Sample

ogre_src_v1-8-1\Samples\Terrain

1.2  类图

<帖不了图图> 

 

1.3  使用流程

1、首先需要创建terrain options

TerrainGlobalOptions* mTerrainGlobals;
TerrainGroup* mTerrainGroup;
mTerrainGlobals = OGRE_NEW TerrainGlobalOptions();
mTerrainGlobals->setMaxPixelError(8);
mTerrainGlobals->setCompositeMapDistance(3000);
 
mTerrainGlobals->setLightMapDirection(l->getDerivedDirection());
mTerrainGlobals->setCompositeMapAmbient(mSceneMgr->getAmbientLight());
mTerrainGlobals->setCompositeMapDiffuse(l->getDiffuseColour());

 

2、其次要创建TerrainGroup对象

mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mSceneMgr, Ogre::Terrain::ALIGN_X_Z, 513, 12000.0f);
mTerrainGroup->setFilenameConvention(Ogre::String("BasicTutorial3Terrain"), Ogre::String("dat"));
mTerrainGroup->setOrigin(Ogre::Vector3::ZERO);

 

3、然后设置Terrain Group

// Configure default import settings for if we use imported image
Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
defaultimp.terrainSize = TERRAIN_SIZE;
defaultimp. worldSize = TERRAIN_WORLD_SIZE;
defaultimp.inputScale = 600;
defaultimp.minBatchSize = 33;
defaultimp.maxBatchSize = 65;
// textures
defaultimp.layerList.resize(3);
defaultimp.layerList[0]. worldSize = 100;
defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_diffusespecular.dds");
defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_normalheight.dds");
defaultimp.layerList[1]. worldSize = 30;
defaultimp.layerList[1].textureNames.push_back("grass_green-01_diffusespecular.dds");
defaultimp.layerList[1].textureNames.push_back("grass_green-01_normalheight.dds");
defaultimp.layerList[2]. worldSize = 200;
defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_diffusespecular.dds");
defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_normalheight.dds");


     4、最后执行加载

 

mTerrainGroup->loadAllTerrains(true)
后续需要计算blendmaps 

6、清理Terrain Group

 

mTerrainGroup->freeTemporaryResources();

Terrain Group是Terrain的集合,如此可以取到集合里的terrain:

TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();

while(ti.hasMoreElements())

{

       Terrain* t = ti.getNext()->instance;

  

至此完成了ogre最新的TerrainGroup的生命周期。

 

1.4  地形文件

 1.4.1  Terrain文件格式 

TerrainData (Identifier 'TERR')
[Version 1]

Name

Type

Description

Terrain orientation

uint8

The orientation of the terrain; XZ = 0, XY = 1, YZ = 2

Terrain size

uint16

The number of vertices along one side of the terrain

Terrain world size

Real

The world size of one side of the terrain

Max batch size

uint16

The maximum batch size in vertices along one side

Min batch size

uint16

The minimum batch size in vertices along one side

Position

Vector3

The location of the centre of the terrain

Height data

float[size*size]

List of floating point heights

LayerDeclaration

LayerDeclaration*

The layer declaration for this terrain (see below)

Layer count

uint8

The number of layers in this terrain

LayerInstance list

LayerInstance*

A number of LayerInstance definitions based on layer count (see below)

Layer blend map size

uint16

The size of the layer blend maps as stored in this file

Packed blend texture data

uint8*

layerCount-1 sets of blend texture data interleaved as either RGB or RGBA depending on layer count

Optional derived map data

TerrainDerivedMap list

0 or more sets of map data derived from the original terrain

Delta data

float[size*size]

At each vertex, delta information for the LOD at which this vertex disappears

Quadtree delta data

float[quadtrees*lods]

At each quadtree node, for each lod a record of the max delta value in the region

 

TerrainLayerDeclaration (Identifier 'TDCL')
[Version 1]

Name

Type

Description

TerrainLayerSampler Count

uint8

Number of samplers in this declaration

TerrainLayerSampler List

TerrainLayerSampler*

List of TerrainLayerSampler structures

Sampler Element Count

uint8

Number of sampler elements in this declaration

TerrainLayerSamplerElement List

TerrainLayerSamplerElement*

List of TerrainLayerSamplerElement structures

TerrainLayerSampler (Identifier 'TSAM')
[Version 1]

Name

Type

Description

Alias

String

Alias name of this sampler

Format

uint8

Desired pixel format

 

TerrainLayerSamplerElement (Identifier 'TSEL')
[Version 1]

Name

Type

Description

Source

uint8

Sampler source index

Semantic

uint8

Semantic interpretation of this element

Element start

uint8

Start of this element in the sampler

Element count

uint8

Number of elements in the sampler used by this entry

 

LayerInstance (Identifier 'TLIN')
[Version 1]

Name

Type

Description

World size

Real

The world size of this layer (determines UV scaling)

Texture list

String*

List of texture names corresponding to the number of samplers in the layer declaration

 

TerrainDerivedData (Identifier 'TDDA')
[Version 1]

Name

Type

Description

Derived data type name

String

Name of the derived data type ('normalmap', 'lightmap', 'colourmap', 'compositemap')

Size

uint16

Size of the data along one edge

Data

varies based on type

The data  

1.4.2  加载地形文件

 

 OgreTerrain_d.dll!Ogre::Terrain::determineLodLevels
OgreTerrain_d.dll!Ogre::Terrain::prepare
OgreTerrain_d.dll!Ogre::Terrain::prepare
OgreTerrain_d.dll!Ogre::TerrainGroup::handleRequest
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::RequestHandlerHolder::handleRequest
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::processRequest
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::processRequestResponse
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::addRequest
OgreTerrain_d.dll!Ogre::TerrainGroup::loadTerrainImpl
OgreTerrain_d.dll!Ogre::TerrainGroup::loadAllTerrains
Sample_Terrain_d.dll!Sample_Terrain::setupContent

 

首先加载全局选项TerrainGlobalOptions

然后从本地terrain文件中读取(@Terrain::prepare)
  

18:12:36: DefaultWorkQueueBase('Root') - QUEUED(thread:main): ID=1 channel=1 requestType=1

18:12:36: DefaultWorkQueueBase('Root') - PROCESS_REQUEST_START(main): ID=1 channel=1 requestType=1

18:12:36: Terrain created; size=513 minBatch=33 maxBatch=65 treeDepth=4 lodLevels=5 leafLods=2

18:12:36: Terrain::distributeVertexData processing source terrain size of 513

18:12:36:   Assigning vertex data, resolution=513 startDepth=2 endDepth=4 splits=4

18:12:36:   Assigning vertex data, resolution=129 startDepth=0 endDepth=2 splits=1

18:12:36: Terrain::distributeVertexData finished

18:12:36: DefaultWorkQueueBase('Root') - PROCESS_REQUEST_END(main): ID=1 channel=1 requestType=1 processed=1

18:12:36: DefaultWorkQueueBase('Root') - PROCESS_RESPONSE_START(thread:main): ID=1 success=1 messages=[] channel=1 requestType=1

18:12:36: Font Default/Vera using texture size 512x256

18:12:36: Info: Freetype returned null for character 160 in font Default/Vera

18:12:36: Texture: Default/VeraTexture: Loading 1 faces(PF_BYTE_LA,512x256x1) with 0 generated mipmaps from Image. Internal format is PF_BYTE_LA,512x256x1.

18:12:36: Mesh: Loading axes.mesh.

18:12:36: WARNING: axes.mesh is an older format ([MeshSerializer_v1.30]); you should upgrade it as soon as possible using the OgreMeshUpgrade tool.

18:12:36: Texture: axes.png: Loading 1 faces(PF_R8G8B8,256x256x1) Internal format is PF_X8R8G8B8,256x256x1.

18:12:36: DefaultWorkQueueBase('Root') - PROCESS_RESPONSE_END(thread:main): ID=1 success=1 messages=[] channel=1 requestType=1 

1.4.3  地形表面网格

 已经不存在一个具体的地形表面网格的概念了,地形是“分层分批处理”的东西,地形对象不再拥有一个具体的地形顶点数据,这些数据是在LODs中的。

 

http://www.ogre3d.org/forums/viewtopic.php?f=11&t=50674&start=275#p365005

Actually, the Terrain object doesn't hold this information. The terrain is what I call "hierarchically batched" which means there is no set of vertex data at the highest LOD which covers the entire terrain - instead there are a series of hierarchical nodes which each store a specific range of LODs, each of which has a different coverage of the terrain. The only batch which has the whole terrain stored in one are the lowest LOD levels - used when the terrain is very far away. This allows us to efficiently render the entire terrain in one batch when far away, but closer up smaller (physically) batches are used for higher LODs but overall the vertex data for each batch is of the same size (or within a small range). This also allows us to deal with terrains that would be impossible to address with 16-bit indexes - any patch with more than 256 vertices on each side is actually impossible to address as one batch anyway without 32-bit indexes, which I avoid for compatibility. My hierarchical batch system allows very large terrain patches while still respecting 16-bit indexes and generally giving better performance. Unfortunately, it can never be as simple as a single top-level set of vertex data.

 

So, if I gave you access to what we use internally, I think you'd just be very confused  You really do just need to extract the raw heights or just walk across the terrain using getPoint() if you want something 'raw'. I suppose I could provide an API which dumps unindexed full-LOD triangles into a buffer (or maybe with 32-bit indexing), but this will be really inefficient if you then have to re-process the buffer yourself anyway. It's much better just to hook out the points and plug those into your system directly.

  

1.5  四叉树结构

       每个叶子节点的size都是允许划分的批次最大size,也即65。它有2个LodLevel,其size是33,这个LodLevel已经是不可划分的批次最小size了。而非叶子节点的size都比允许的批次最大size大,并且它只有一个LodLevel,其size也是批次最大size。

       由此可见,允许的批次最大和最小size是划分树节点和LodLevel的直接依据,它们约束了节点和Lod划分的顶点尺寸。对于树节点,简单来说划分的方法是将其平均分割成4块,如果每块比允许的批次最大size还要大,则用同样的方式对它再次递归分割。对于Lod来说,如果其所有者树节点不是叶子,那这个Lod就让其大小设为批次的最大size,否则,就对其进行Lod细分,让每个Lod尽量的小,但是不能小于批次允许的最小值。

       批次最大最小size约束存在的意义是,一方面让每个lod的顶点尽可能的少,这样在渲染的时候可以更准确的找出最少的空间分割块,以便选择尽可能少的顶点;另一方面每个Lod的顶点又不能太少,否则会增加显卡的渲染批次。

 

 +[0]New Node,size:513,lod:4,depth:0,quadrant:0,batch range[33,65]
   +[1]New Node,size:257,lod:3,depth:1,quadrant:0,batch range[33,65]
            +[2]New Node,size:129,lod:2,depth:2,quadrant:0,batch range[33,65]
                  +[ 3]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[ 4]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[ 5]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[ 6]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[7]New Node,size:129,lod:2,depth:2,quadrant:1,batch range[33,65]
                  +[ 8]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[ 9]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[10]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[11]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[12]New Node,size:129,lod:2,depth:2,quadrant:2,batch range[33,65]
                  +[13]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[14]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[15]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[16]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[17]New Node,size:129,lod:2,depth:2,quadrant:3,batch range[33,65]
                  +[18]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[19]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[20]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[21]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
       +[22]New Node,size:257,lod:3,depth:1,quadrant:1,batch range[33,65]
            +[23]New Node,size:129,lod:2,depth:2,quadrant:0,batch range[33,65]
                  +[24]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[25]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[26]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[27]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[28]New Node,size:129,lod:2,depth:2,quadrant:1,batch range[33,65]
                  +[29]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[30]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[31]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[32]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[33]New Node,size:129,lod:2,depth:2,quadrant:2,batch range[33,65]
                  +[34]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[35]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[36]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[37]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[38]New Node,size:129,lod:2,depth:2,quadrant:3,batch range[33,65]
                  +[39]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[40]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[41]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[42]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
       +[43]New Node,size:257,lod:3,depth:1,quadrant:2,batch range[33,65]
            +[44]New Node,size:129,lod:2,depth:2,quadrant:0,batch range[33,65]
                  +[45]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[46]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[47]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[48]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[49]New Node,size:129,lod:2,depth:2,quadrant:1,batch range[33,65]
                  +[50]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[51]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[52]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[53]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[54]New Node,size:129,lod:2,depth:2,quadrant:2,batch range[33,65]
                  +[55]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[56]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[57]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[58]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[59]New Node,size:129,lod:2,depth:2,quadrant:3,batch range[33,65]
                  +[60]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[61]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[62]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[63]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
       +[64]New Node,size:257,lod:3,depth:1,quadrant:3,batch range[33,65]
            +[65]New Node,size:129,lod:2,depth:2,quadrant:0,batch range[33,65]
                  +[66]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[67]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[68]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[69]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[70]New Node,size:129,lod:2,depth:2,quadrant:1,batch range[33,65]
                  +[71]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[72]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[73]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[74]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[75]New Node,size:129,lod:2,depth:2,quadrant:2,batch range[33,65]
                  +[76]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[77]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[78]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[79]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
            +[80]New Node,size:129,lod:2,depth:2,quadrant:3,batch range[33,65]
                  +[81]New Node,size: 65,lod:1,depth:3,quadrant:0,batch range[33,65]
                  +[82]New Node,size: 65,lod:1,depth:3,quadrant:1,batch range[33,65]
                  +[83]New Node,size: 65,lod:1,depth:3,quadrant:2,batch range[33,65]
                  +[84]New Node,size: 65,lod:1,depth:3,quadrant:3,batch range[33,65]
 

 

Depth

Size

LOD

节点数

0

513

4

1

1

257

3

4

2

129

2

16

3

65

1

64

 

1.5.1  叶子节点

叶子节点的顺序如下

1

2

 

 

 

 

 

 

3

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

每个节点记录了一个当前节点的偏移值,这个偏移值是相对于父节点的继承偏移方式计算出的数值,有点类似场景管理器中的Node._getDerivedPosition,也即是相对于当前地形的偏移值,准确来说是相对于当前地形左上角的偏移,而不是相对其父节点。这个偏移值需要注意2点:

u 偏移用+x向下,+y向左的二维坐标系计算

u 偏移值与顶点数有-1差,也即最小的格子(LOD=1)的宽度的整数倍,当前是64。

  

1.6  预计算基础数据

首先,预先计算好一些全局基础数据,例如树深度,最大Lod,叶子节点的Lod。见Terrain::determineLodLevels。例如,计算结果如下:

 

18:12:36: DefaultWorkQueueBase('Root') - QUEUED(thread:main): ID=1 channel=1 requestType=1

18:12:36: DefaultWorkQueueBase('Root') - PROCESS_REQUEST_START(main): ID=1 channel=1 requestType=1

18:12:36: Terrain created; size=513 minBatch=33 maxBatch=65 treeDepth=4 lodLevels=5 leafLods=2

18:12:36: Terrain::distributeVertexData processing source terrain size of 513

18:12:36:   Assigning vertex data, resolution=513 startDepth=2 endDepth=4 splits=4

18:12:36:   Assigning vertex data, resolution=129 startDepth=0 endDepth=2 splits=1

18:12:36: Terrain::distributeVertexData finished

18:12:36: DefaultWorkQueueBase('Root') - PROCESS_RESPONSE_END(thread:main): ID=1 success=1 messages=[] channel=1 requestType=1

 

由于我们预先定义好了批次中最大最小顶点数、地形的顶点尺寸(分别是65,33,513),通过这个预先定义好的值,可以生成一个基于四叉树的LOD关系数据结构。这个数据结构可以“映射”到任意面积尺寸的地形中,例如一个单边为12000的正方形的地形。

  

1.7  分配地形顶点

地形定义都取到后,就开始分配地形顶点数据了。

OgreTerrain_d.dll!Ogre::Terrain::distributeVertexData
OgreTerrain_d.dll!Ogre::Terrain::prepare
OgreTerrain_d.dll!Ogre::Terrain::prepare
OgreTerrain_d.dll!Ogre::TerrainGroup::handleRequest
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::RequestHandlerHolder::handleRequest
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::processRequest
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::processRequestResponse
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::addRequest
OgreTerrain_d.dll!Ogre::TerrainGroup::loadTerrainImpl
OgreTerrain_d.dll!Ogre::TerrainGroup::loadAllTerrains
Sample_Terrain_d.d 


(分配顶点的流程图...只能看pdf了) 

 

1.7.1  算法思想 

现在需要找出如何分配顶点数据。我们要兼容16位的索引,这意味着我们最多可以拼凑129x129个地形,即使是松散的拼凑低细节的LODs也如此,因为下一个可拼凑数是257x257,这个数字太大了,所以不用它。

 

因此,我们需要将顶点数据分割成129块。主要地砖上创建的的数目也即表明了它上面的点,如果不使用其他的顶点数据我们在树节点中就不能在更低的LODs中合并地砖了。例如,使用如上所述的257x257的输入,顶点数据为了适合129x129的范围将不得不被分为2个(在每个维度上)。这些数据可以被树深度从1开始的所有的树共享,不过Lods 3-1将会从129x129的稀疏数据中采样,而LOD0将会从所有顶点数据中采样。

 

然而,最低的LOD4将不能使用同样的顶点数据进行处理,因为它需要覆盖整个地形。这里有2个选择:在17x17上创建另一组仅用于LOD4的顶点数据的,或者在出现树深度为1的时候用LOD4(例如仍旧分拆),并且沿着每一边像2x9一样渲染。

 

由于渲染非常小的批次不理想,以及顶点总数本质上不会很大,所以创建一个单独的顶点集还是有可行性。在出现遥远的地形时也将会让顶点缓存机制更高效。

 

我们可能需要一个更大尺寸的例子,因为在这种情况下只有层级1(LOD0)需要使用这种单独的顶点数据。较高细节的地形将会需要多种层次,这里有一个65/33批次设置的2049x2049的例子:

 

LODlevels = log2(2049 - 1) - log2(33 - 1) + 1 = 11 - 5 + 1 = 7

TreeDepth = log2((2049 - 1) / (65 - 1)) + 1 = 6

 

在最多细节层次上拆分的顶点数

 

(size - 1) / (TERRAIN_MAX_BATCH_SIZE - 1) = 2048 / 128 = 16

 

 

 

 

 

 

 

 

 

 

 

LOD

0:

2049

vertices

32 x 65

vertex

tiles

(tree depth 5)

vdata

0-15

[129x16]

LOD

1:

1025

vertices

32 x 33

vertex

tiles

(tree depth 5)

vdata

0-15

[129x16]

LOD

2:

513

vertices

16 x 33

vertex

tiles

(tree depth 4)

vdata

0-15

[129x16]

LOD

3:

257

vertices

8 x 33

vertex

tiles

(tree depth 3)

vdata

16-17

[129x2]

LOD

4:

129

vertices

4 x 33

vertex

tiles

(tree depth 2)

vdata

16-17

[129x2]

LOD

5:

65

vertices

2 x 33

vertex

tiles

(tree depth 1)

vdata

16-17

[129x2]

LOD

6:

33

vertices

1 x 33

vertex

tiles

(tree depth 0)

vdata

18

[33]

 

所有的顶点总数都是一个平方数,它们正好是沿着一条边的情形。所以,你可以看到我们需要有3个级别的顶点数据来满足(诚然,相当极端)这个情况,并且一共有19个顶点数据集。完整的细节几何,129的16个子集(X16)这样的有完全细节的几何体被用作LODs0-2。 LOD3不能使用这个子集,因为它需要通过这些子集进行组合,而且它只有8块地砖,所以我们需要在每一个顶点数据段最大是129个顶点的时候构造出另外一个集合来满足这个情况。因为在这种情况下LOD3需要整个257(X257)个的顶点,所以我们仍然将129分割成2(X2)个集合。虽然这一套集合是好用了,也包括了LOD5,但LOD6需要一个单一且连续的顶点集,所以我们为它构造了一个33x33的顶点集。

 

在顶点的数据存储方面,这意味着当我们的主要数据是:

2049^ 2 =4198401个顶点

最终我们存储的顶点数据是

(16 *129)^ 2+(2* 129)^ 2+ 33^ 2 =4327749个顶点

 

这相当于有3%的顶点冗余,但是为了从分组中减少批次它既必要又值得。此外,在LODs3和6(或树深度为3和0)中将有机会释放被更多细节LODs使用的数据,这在有巨大的地形的时候很重要。例如,如果我们在中等距离时为LOD0-2释放(GPU)顶点数据,就会为地形节省平均有98%的内存开销。

 

1.7.2  顶点数据

LODs在当前4叉树节点构造时候被创建,其中有个字段指向顶点数据,这个顶点数据在读取地形文件时被创建。

 

J 创建顶点数据

   //---------------------------------------------------------------------
   void TerrainQuadTreeNode::createGpuIndexData()
   {
      for (size_t lod = 0; lod < mLodLevels.size(); ++lod)
      {
        LodLevel* ll = mLodLevels[lod];
 
        if (!ll->gpuIndexData)
        {
           // clone, using default buffer manager ie hardware
           ll->gpuIndexData = OGRE_NEW IndexData();
           populateIndexData(ll->batchSize, ll->gpuIndexData);
        }
 
      }
   }

 

 

J 取回顶点数据

   //---------------------------------------------------------------------
   //渲染数据
   void TerrainQuadTreeNode::getRenderOperation(RenderOperation& op)
   {
      mNodeWithVertexData->updateGpuVertexData();
 
      op.indexData = mLodLevels[mCurrentLod]->gpuIndexData;
      op.operationType = RenderOperation::OT_TRIANGLE_STRIP;
      op.useIndexes = true;
      op.vertexData = getVertexDataRecord()->gpuVertexData;
   }
 
 

 

 

1.7.3  同步-异步机制

异步加载机制是Steven Streeting离开前奉献的一个重量级模块Paging的核心功能,Paging的异步加载实现了一个通用的分页机制,目前只知道在地形中有应用。但是这个优秀的分页机制可以在所有时间、帧率、消息、事件、渲染命令出现瓶颈的时候使用其扩展的各种灵活策略进行异步和有区分度的分解处理从而降低单帧负载。其算法思想Steven Streeting在Ogre官网有详细阐述,见章节“Page系统设计思想”。

       为了区别异步加载给地形带来的复杂度,关闭了异步线程和地形Paging 。

 #define OGRE_THREAD_SUPPORT 0

 
//#define PAGING
 

        Root维护一个默认的工作队列DefaultWorkQueue,执行类似压入执行命令的逻辑用这个工作队列完成。由于关闭了异步,压入请求后会立即执行响应请求的子程序。

 

Class DefaultWorkQueue : public DefaultWorkQueueBase
OgreTerrain_d.dll!Ogre::TerrainQuadTreeNode::load
OgreTerrain_d.dll!Ogre::Terrain::load
OgreTerrain_d.dll!Ogre::TerrainGroup::handleResponse
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::processResponse
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::processRequestResponse
OgreMain_d.dll!Ogre::DefaultWorkQueueBase::addRequest
OgreTerrain_d.dll!Ogre::TerrainGroup::loadTerrainImpl
OgreTerrain_d.dll!Ogre::TerrainGroup::loadAllTerrains
Sample_Terrain_d.dll!Sample_Terrain::setupContent
Sample_Terrain_d.dll!OgreBites::SdkSample::_setup
 

 

1.8  动态LOD计算

动态LOD计算主要目的是通过相机与四叉树节点的相对关系计算出几个关键指标:

u 当前Lod,标记当前节点LodLevel中第N个被使用的Lod

u 当前节点是否渲染,标记当前节点带领的子树是否有节点需要被渲染

 

LodLevel定义

      Struct LodLevel{
        /// Number of vertices rendered down one side (not including skirts)
        uint16 batchSize;
        /// Index data on the gpu
        IndexData* gpuIndexData;
        /// Maximum delta height between this and the next lower lod
        Real maxHeightDelta;
        /// Temp calc area for max height delta
        Real calcMaxHeightDelta;
        /// The most recently calculated transition distance
        Real lastTransitionDist;
        /// The cFactor value used to calculate transitionDist
        Real lastCFactor;
 
        LodLevel() : gpuIndexData(0), maxHeightDelta(0), calcMaxHeightDelta(0),
           lastTransitionDist(0), lastCFactor(0) {}
      };

 

 

1.8.1  LodLevel数据细节

一个节点聚合了4个LOD相关属性

 

 

基础Lod(Base Lod)

预先计算好,最深的叶子节点是0,然后由内向外递增,且兄弟节点一样

当前Lod(Current Lod)

动态计算,@TerrainQuadTreeNode::calculateCurrentLod

Lod层级(Lod Level)

保存Lod对应的具体顶点信息,每个节点“挂”一个或多个LodLevel

Lod层级列表(Lod Level List)

 

 

所有节点的Lod相关属性大部分都是初始化时就计算好,只有当前LOD是动态计算的。非叶子节点只有一个LodLevel,其顶点数量(LodLevel.batchSize)是当前terrain的批次顶点最小值;非叶子节点的基础Lod是父节点的基础Lod-1。叶子节点有多个LodLevel,其数量是由当前terrain预先计算好(NumLodLevelsPerLeaf),每个LodLevel中的顶点数量(LodLevel.batchSize)是当前terrain的批次顶点最大值(MaxBatchSize)------与非叶子节点的情况正好相反;叶子节点的基础LOD总是0。

可以看到,节点的Lod值由内从0开始向外逐渐递增,且同一深度(depth)的节点Lod也一样。而每个节点都会“挂”上一个或多个LodLevel,非叶子节点只“挂”一个,叶子节点“挂”多个,这个数量是有terrain根据世界尺寸、最大、最小单批次顶点数等值预先计算好的。

非叶子节点“挂”的LodLevel所包含的顶点数是terrain的允许的单个批次顶点最小数量。叶子节点的情况则不同,“挂”的第一个LodLevel所包含的顶点数是单个批次顶点的最大数,然后第二个减少一半---实际情况稍微复杂,数量由公式(((sz - 1) * 0.5) + 1)给出,也即几何学上的四边形顶点减半。有点类似D3DX中的层级纹理。

       一切都很天衣无缝,只等当前Lod动态计算时,按照特定的规则决定哪些顶点需要被渲染,也即哪些LodLevel参与渲染。

 

1.8.2  动态计算Lod

一般的,Lod表示的值从0开始,依次递增,越到后面对象的细节越少。典型的如层级纹理。在地形中也如此。首先,在整个四叉树节点中,每个节点有自己的Lod,或者叫基础Lod,这个值是固定的:叶子节点的Lod占用0~M;兄弟关系的Lod一样;非叶子节点占用1个Lod,从M+1开始,越往树根走Lod越大。这样Lod最大的节点就是树根了。

       动态计算Lod的关键因素是相机到节点中心的距离与节点中LodLevel的过渡距离,后者好比是一把尺子,用于判断这个Lod是否可以被相机可见。计算方法见TerrainQuadTreeNode::calculateCurrentLod。

 

 

每帧地形的Lod计算过程是对四叉树递归遍历的过程。首先遍历所有子树,然后在进行自身的计算。将计算过错分解为第一类计算过程和第二类计算过程:

bool TerrainQuadTreeNode::calculateCurrentLod(const Camera* cam, Real cFactor)
{
  mSelfOrChildRendered = false;
  ///------------------------------------------------------------------------
  //深度优先的遍历,首先检查第一个叶子节点,考察其可见性,然后是兄弟节点,软后是父节点
  int childRenderedCount = 0;
  if (!isLeaf())
  {
    for (int i = 0; i < 4; ++i)
    {
       if (mChildren[i]->calculateCurrentLod(cam, cFactor))
          ++childRenderedCount;
    }

  }
 

  //需要渲染的子节点数是0,或者是叶子节点,或者是所有子节点都不参与渲染的子树
  //所有叶子节点都不需要渲染,那就只有考察节点自身是否需要渲染了
  if (childRenderedCount == 0)
  {

    ///  第一类计算过程  ///

  }
  //当前节点有子节点参与渲染,那自身就不需要参与渲染了,
  //如果需要渲染的子节点数量大于或等于4,只需做个标记,不用再计算当前节点了
  else
  {
    ///  第二类计算过程  ///

    //跳过自身的渲染
    mCurrentLod = -1;
    //当前子树需要被渲染
    mSelfOrChildRendered = true;
    //只考虑需要渲染的子节点小于4的情形
    if (childRenderedCount < 4)
    {
       // only *some* children decided to render on their own, but either
       // none or all need to render, so set the others manually to their lowest
       for (int i = 0; i < 4; ++i)
       {
          TerrainQuadTreeNode* child = mChildren[i];
          if (!child->isSelfOrChildRenderedAtCurrentLod())
          {
             child->setCurrentLod(child->getLodCount()-1);
             child->setLodTransition(1.0);
          }
       }
    } // (childRenderedCount < 4)
  } // (childRenderedCount == 0)
}

 

 

1.8.3  LOD第一类计算过程

 // no children were within their LOD ranges, so we should consider our own
Vector3 localPos = cam->getDerivedPosition() - mLocalCentre - mTerrain->getPosition();
       
//相机到当前节点中心的绝对距离
Real dist = localPos.length();
dist -= (mBoundingRadius * 0.5f);
 
// For each LOD, the distance at which the LOD will transition *downwards*
// is given by
// distTransition = maxDelta * cFactor;
 
uint lodLvl = 0;
mCurrentLod = -1;
for (LodLevelList::iterator i = mLodLevels.begin(); i != mLodLevels.end(); ++i, ++lodLvl)
{
   // If we have no parent, and this is the lowest LOD, we always render
   // this is the 'last resort' so to speak, we always enoucnter this last
   if (lodLvl+1 == mLodLevels.size() && !mParent)
   {
      mCurrentLod = lodLvl;
      mSelfOrChildRendered = true;
      mLodTransition = 0;
   }
   else
   {
      // check the distance
 
      ///------------------------------------------------------------------------
      //计算过渡距离distTransition
      // Calculate or reuse transition distance
      Real distTransition;
      LodLevel* ll = *i;
      if (Math::RealEqual(cFactor, ll->lastCFactor))
        distTransition = ll->lastTransitionDist;
      else
      {
        distTransition = ll->maxHeightDelta * cFactor;
        ll->lastCFactor = cFactor;
        ll->lastTransitionDist = distTransition;
      }
 
      ///------------------------------------------------------------------------
      //相机是否离Lod足够近
      //相机到节点中心的距离小于过渡距离,则让其显示
      //对于叶子节点,Lod0的过渡距离小于Lod1
      if (dist < distTransition)
      {
        // we're within range of this LOD
        mCurrentLod = lodLvl;
        mSelfOrChildRendered = true;
 
        // Lod在节点中的存储顺序是用最高细节Lod到最低细节Lod的顺序
        // 碰到第一个Lod就结束了,因为这个Lod细节已经是最高了
        // 一般是叶子的第一个Lod,也即Lod0
        break;
      }//~相机足够近
 
   }
}//~foreach LodLevelList  

1.8.4  LOD第二类计算过程

//跳过自身的渲染;we should not render ourself
mCurrentLod = -1;
//当前子树需要被渲染
mSelfOrChildRendered = true;
//只考虑需要渲染的子节点小于4的情形
if (childRenderedCount < 4)
{
   // only *some* children decided to render on their own, but either
   // none or all need to render, so set the others manually to their lowest
   for (int i = 0; i < 4; ++i)
   {
      TerrainQuadTreeNode* child = mChildren[i];
      if (!child->isSelfOrChildRenderedAtCurrentLod())
      {
        child->setCurrentLod(child->getLodCount()-1);
        child->setLodTransition(1.0);
      }
   }
} // (childRenderedCount < 4)
 

第二类计算步骤比较简单,当前节点有子节点参与渲染,那自身就不需要参与渲染了。首先标记下这个子树需要被渲染,然后考察其递归子节点需要被渲染的数量,如果数量在[1,3]这个区间,则需要处理这个节点的4个直接子节点。

需要处理当前节点的4个直接子节点的了,如果这个子节点包含自身的树都不需要渲染,则将这个节点的Lod数量-1。之前在预计算全局基础数据时已经知道,就当前这个实例而言,叶子节点的Lod数量是2,非叶子节点的Lod数量是0。这样,对于当前节点的4个子树,如果这个子树不参与渲染,则其当前Lod=0。

  

1.9  渲染

地形四叉树的渲染使用了2个小技巧。一是每个树节点Hook了一个内嵌类TerrainQuadTreeNode.Rend和Movable参与到场景的管理,而它们并不是一个真实的场景对象和渲染对象,可以将它们理解为很多人喜欢使用的虚拟对象,在真正需要渲染的时候,将逻辑还是传递给TerrainQuadTreeNode。二是地形监听了场景管理器预渲染方法,这个方法正好在场景管理器八叉树遍历场景对象前执行。好处显而易见:让复杂的程序结构清晰易读。

首先,地形四叉树只应用于到地形,而不干涉场景。地形在渲染方面主要做了三件事,一是构建了一个四叉树和对应的Lod;二是构建了与每个Lod关联的顶点数据;三是每个四叉树节点都构造一个影子render对象和movable对象。

影子moveable对象在地形初次load的时候构建完成,每个四叉树节点都会新建一个场景节点,并关节上这个引子movable对象,这样让每个地形四叉树节点参与到场景八叉树节点的可见性计算中(当前考虑的场景管理器是默认的八叉树场景管理器)。

 

而在最开始,地形对象监听了场景管理器的SceneManager.firePreFindVisibleObjects方法,而这个方法正好仅仅在计算场景可见渲染对象的前一步:

 Class Terrain : public SceneManager::Listener

 
void SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)
{
   
    firePreFindVisibleObjects(vp);
 
   findVisibleObjects(camera
);
   firePostFindVisibleObjects(vp);
   
 
    // Begin the frame
    mDestRenderSystem->_beginFrame();
 
    // Set rasterisation mode
    mDestRenderSystem->_setPolygonMode(camera->getPolygonMode());
 
   // Set initial camera state
   mDestRenderSystem->_setProjectionMatrix(mCameraInProgress->getProjectionMatrixRS());
  
  
    // Render scene content
    renderVisibleObjects();
  
    // End frame
mDestRenderSystem->_endFrame();
 
 ......

 

于是在渲染场景的时候,每个地形的四叉树节点由2个紧邻的分计算完成。首先预计算地形Lod,然后的计算其实是一个通用的场景管理器计算过程,地形并未与场景中其他节点有所不同。

OgreTerrain_d.dll!Ogre::TerrainQuadTreeNode::calculateCurrentLod
OgreTerrain_d.dll!Ogre::Terrain::calculateCurrentLod
OgreTerrain_d.dll!Ogre::Terrain::preFindVisibleObjects
OgreMain_d.dll!Ogre::SceneManager::firePreFindVisibleObjects
OgreMain_d.dll!Ogre::SceneManager::_renderScene
OgreMain_d.dll!Ogre::Camera::_renderScene
OgreMain_d.dll!Ogre::Viewport::update
OgreMain_d.dll!Ogre::RenderTarget::_updateViewport
RenderSystem_Direct3D9_d.dll!Ogre::D3D9RenderWindow::_updateViewport
OgreMain_d.dll!Ogre::RenderTarget::_updateAutoUpdatedViewports
OgreMain_d.dll!Ogre::RenderTarget::updateImpl
OgreMain_d.dll!Ogre::RenderTarget::update
OgreMain_d.dll!Ogre::RenderSystem::_updateAllRenderTargets
OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets
OgreMain_d.dll!Ogre::Root::renderOneFrame

(地形预计算Lod )

 

OgreMain_d.dll!Ogre::RenderQueue::addRenderable
OgreTerrain_d.dll!Ogre::TerrainQuadTreeNode::updateRenderQueue
OgreTerrain_d.dll!Ogre::TerrainQuadTreeNode::Movable::_updateRenderQueue
OgreMain_d.dll!Ogre::RenderQueue::processVisibleObject
Plugin_OctreeSceneManager_d.dll!Ogre::OctreeNode::_addToRenderQueue
Plugin_OctreeSceneManager_d.dll!Ogre::OctreeSceneManager::walkOctree
Plugin_OctreeSceneManager_d.dll!Ogre::OctreeSceneManager::walkOctree
Plugin_OctreeSceneManager_d.dll!Ogre::OctreeSceneManager::walkOctree
Plugin_OctreeSceneManager_d.dll!Ogre::OctreeSceneManager::walkOctree
Plugin_OctreeSceneManager_d.dll!Ogre::OctreeSceneManager::_findVisibleObjects
OgreMain_d.dll!Ogre::SceneManager::_renderScene
OgreMain_d.dll!Ogre::Camera::_renderScene
OgreMain_d.dll!Ogre::Viewport::update
OgreMain_d.dll!Ogre::RenderTarget::_updateViewport
RenderSystem_Direct3D9_d.dll!Ogre::D3D9RenderWindow::_updateViewport
OgreMain_d.dll!Ogre::RenderTarget::_updateAutoUpdatedViewports
OgreMain_d.dll!Ogre::RenderTarget::updateImpl
OgreMain_d.dll!Ogre::RenderTarget::update
OgreMain_d.dll!Ogre::RenderSystem::_updateAllRenderTargets
OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets
OgreMain_d.dll!Ogre::Root::renderOneFrame

 

(场景管理器通用渲染过程)

       实际计算场景中的可见对象时,如果当前地形四叉树的影子Movable对象在相机中可见,那么只要这个四叉树节点中的当前Lod不是-1,就将其加入到渲染队列。还记得当前Lod表示的时候这个四叉树节点LodLevel层级中需要被渲染的那个Lod,这样在这里,这个渲染对象还是间接的渲染的一个LodLevel,这个真实的渲染对象在于场景管理器打交道的时候,表现为它的影子渲染对象TerrainQuadTreeNode.Rend。

 

       如此这般,场景中需要渲染的对象都组装好了,好好的躺在渲染队列中。万事俱备只欠东风,是时候渲染了,这个渲染方法SceneManager.renderVisibleObjects在场景对象的预计算、实际计算两个步骤之后。

OgreTerrain_d.dll!Ogre::TerrainQuadTreeNode::getRenderOperation
OgreTerrain_d.dll!Ogre::TerrainQuadTreeNode::Rend::getRenderOperation
OgreMain_d.dll!Ogre::SceneManager::renderSingleObject
OgreMain_d.dll!Ogre::SceneManager::SceneMgrQueuedRenderableVisitor::visit
OgreMain_d.dll!Ogre::QueuedRenderableCollection::acceptVisitorGrouped
OgreMain_d.dll!Ogre::QueuedRenderableCollection::acceptVisitor
OgreMain_d.dll!Ogre::SceneManager::renderObjects
OgreMain_d.dll!Ogre::SceneManager::renderBasicQueueGroupObjects
OgreMain_d.dll!Ogre::SceneManager::_renderQueueGroupObjects
OgreMain_d.dll!Ogre::SceneManager::renderVisibleObjectsDefaultSequence
OgreMain_d.dll!Ogre::SceneManager::_renderVisibleObjects
OgreMain_d.dll!Ogre::SceneManager::_renderScene
OgreMain_d.dll!Ogre::Camera::_renderScene
OgreMain_d.dll!Ogre::Viewport::update
OgreMain_d.dll!Ogre::RenderTarget::_updateViewport
RenderSystem_Direct3D9_d.dll!Ogre::D3D9RenderWindow::_updateViewport
OgreMain_d.dll!Ogre::RenderTarget::_updateAutoUpdatedViewports
OgreMain_d.dll!Ogre::RenderTarget::updateImpl
OgreMain_d.dll!Ogre::RenderTarget::update
OgreMain_d.dll!Ogre::RenderSystem::_updateAllRenderTargets
OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets
OgreMain_d.dll!Ogre::Root::renderOneFrame

 

//渲染数据
void TerrainQuadTreeNode::getRenderOperation(RenderOperation& op)
{
   mNodeWithVertexData->updateGpuVertexData();
 
   op.indexData = mLodLevels[mCurrentLod]->gpuIndexData;
   op.operationType = RenderOperation::OT_TRIANGLE_STRIP;
   op.useIndexes = true;
   op.vertexData = getVertexDataRecord()->gpuVertexData;
}

 

 

 

       之前说过,加入到渲染队列的是影子渲染对象,真实渲染对象是一个LodLevel,这是一个很好的桥接模式,避免了地形四叉树场景八叉树之间的耦合。其实实现原理也非常简单,不能直接交互的2个对象之间需要交互,就让可以变通的对象投其所好,构建一个让另外那个对象熟悉的影子对象给它使用,只是在这个引子对象被访问的时候,将访问权限还是还给它的“真身”。

       这样在渲染具体的LodLevel的时候,立即通过当前Lod标记找到这个具体的LodLevel,装配好需要的数据交给GPU完成图形设备的渲染流程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

转:Ogre TerrainGroup地形赏析 的相关文章

  • Ubuntu运行rviz报错:OGRE EXCEPTION(3:RenderingAPIException): Unable to create a suitable GLXContext

    写在前面 笔者的测试环境 xff1a Ubuntu20 04 xff0c ROS noteic 一 问题描述 运行rviz时出现报错 xff1a span class token punctuation span WARN span cla
  • Ogre-渐变背景色(gradient background)的实现

    转载自 xff1a http blog csdn net hefee article details 6287341 背景色在ogre里面是通过ViewPort类中的setBackgroundColour xff08 xff09 这个成员函
  • Ogre 界面(Overlay)

    Ogre 界面 Overlay 标签 border脚本buttontemplatesplugins引擎 2011 10 16 23 12 4725人阅读 评论 2 收藏 举报 分类 引擎技术 9 这里的界面是指菜单 HUD及提示信息框在内的
  • 纹理(Textures)

    纹理 Textures 原文 Textures 作者 JoeyDeVries 翻译 Django 校对 Geequlim BLumia 我们已经了解到 我们可以为每个顶点使用颜色来增加图形的细节 从而创建出有趣的图像 但是 如果想让图形看起
  • 可编程渲染管线与着色器语言

    可编程渲染管线与着色器语言 Programming pipeline shading language Sampler Direct3D 9 asm ps A sampler is a input pseudo register for a
  • tinyxml开发入门

    概述 tinyxml和xercesc一样 提供了完整的dom操作api tinyxml相对比较简单好用 编译连接也不容易出问题 xercesc比较麻烦 非常完整庞大 编译有点麻烦 有内存泄漏 我认为在一般需求完全可以使用tinyxml ti
  • ogre引擎0.12.0抄写记录

    惊喜地发现 文档齐全 可以参考类图抄 C Users Legion Desktop ogre v0 12 0 ogrenew Docs api html hierarchy html 先进行 1 OgreMain 然后 2 RenderSy
  • Ogre学习记录

    Ogre学习记录 Posted on 2008 07 09 15 36 RichardHe 阅读 839 评论 2 编辑 收藏 引用 所属分类 OGRE 1 设计初衷 它设计初衷是完全跨平台的 抽象的接口隐藏了平台相关的细节 它设计初衷是大
  • 地形平滑算法

    地形平滑算法 2013 06 20 18 33 2486人阅读 评论 0 收藏 举报 分类 OGRE 41 C C 10 版权声明 本文为博主原创文章 未经博主允许不得转载 拉出来的地下不平滑怎么办 笨办法就是美工一个顶点一个顶点的调整 而
  • 5 个免费开源的 3D 建模/渲染工具。

    5 个开源 3D 建模 渲染工具 3八 2011 作者 riku 本文采用 CC BY NC SA 2 5协议授权 转载请注明 本文链接 5 个免费开源的 3D 建模 渲染工具 1 Art of Illusion 跨平台 支持 Window
  • 地形纹理Splatting技术(翻译)

    地形纹理Splatting技术 翻译 文章来源 http www gamedev net reference articles article2238 asp Texture Splatting in Direct3D Introducti
  • VC 运行时库 /MD、/MDd 和 /MT、/MTd

    VC 运行时库 MD MDd 和 MT MTd 2013 01 24 18 42 11058人阅读 评论 2 收藏 举报 分类 C C 19 有段时间在写cuda程序是出现过 error LNK2005 exit 已经在 MSVCRTD l
  • 转:Ogre TerrainGroup地形赏析

    转 Ogre TerrainGroup地形赏析 1 1 参考 http www ogre3d org tikiwiki tiki index php page Ogre Terrain System http www ogre3d org
  • hlsl register

    https msdn microsoft com en us library dd607359 v VS 85 aspx register Optional keyword for assigning a shader variable t
  • DirectD3D-纹理映射

    DirectD3D 纹理映射 标签 Direct3Ddirectx游戏游戏开发 2014 11 12 14 03 321人阅读 评论 0 收藏 举报 分类 DirectX 8 版权声明 本文为博主原创文章 未经博主允许不得转载 纹理映射的概
  • OGRE动画

    动画是由若干静态画面 快速交替显示而成 因人的眼睛会产生视角暂留 对上一个画面的感知还末消失 下一张画面又出现 就会有动的感觉 计算机图形学中的动画也同样遵循着这一本质的原理 只不过不同于传统动画的手绘和拍摄 Ogre图形引擎可以通过自动或
  • 学习笔记:多重纹理

    学习笔记 多重纹理 2009 09 01 14 20 52 转载 分类 学习笔记 多重纹理 多重纹理就是在渲染一个多边形的时候可以用到多张纹理图 把多张纹理图进行一些颜色的操作 可以达到一些效果 但是多重纹理必须是在显卡支持的情况下 但是还
  • 2021年11月6日-11月12日(ogre抄写+ue4视频,本周35小时,共1035小时,剩8965小时。)

    这周还不错 不但完成了本周学习任务 还完成了本月学习任务 方法就是 拼命抄源码 抄到吐时就再看看Ue4视频教程 内外兼修 可以在未来的日子里这么进行 每天5小时学习 还是进入状态的 5 7 35小时 共1035小时 剩8965小时 另外 去
  • 第十三章 公告板与粒子系统 标签: ogre公告板粒子系统ogre粒子系统

    Ogre编程入门与进阶 第十三章 公告板与粒子系统 标签 ogre公告板粒子系统ogre粒子系统 2015 07 05 14 41 1365人阅读 评论 1 收藏 举报 分类 Orge模块 16 版权声明 本文为博主原创文章 未经博主允许不
  • Ogre g++ 编译时错误

    在ubuntu上 我已经编译并安装了所有的ogre库 但是 当我尝试使用此命令编译教程时 g o otest ogre cpp I pkg config libs cflags OGRE OIS lOgreMain lOI 它输出 tmp

随机推荐

  • odoo连接器-odoo数据拉取,Java xml-rpc实现

    背景 odoo数据拉取 创建 更新 参考 官方external api文档 External API Odoo 14 0 文档 术语 ORM odoo数据以对象模型呈现 支持one2many many2one many2many等对象关联关
  • FSDataOutputStream 的深入分析

    对于一般文件 都有满足随机读写的api 而hadoop中的读api很简单用FSDataInputStream类就可以满足一般要求 而hadoop中的写操作却是和普通java操作不一样 在这里插入代码片 Hadoop对于写操作提供了一个类 F
  • 刷脸支付服务商重金之下必有勇夫

    为了吸引消费者使用刷脸支付 而非扫码 支付宝和微信会给消费者提供优惠 比如在店里面使用刷脸会有随机立减 打折活动 而扫码则没有 这只是给消费者的补贴 以利益吸引商家与推广人员加入刷脸支付同样重要 显然 与二维码支付的几乎没有成本不同 刷脸支
  • C++全局变量的声明和定义

    参考 http wrchen blog sohu com 71617539 html 1 编译单元 模块 在VC或VS上编写完代码 点击编译按钮准备生成exe文件时 编译器做了两步工作 第一步 将每个 cpp c 和相应的 h文件编译成ob
  • 算法学习:插值型求积公式

    算法学习 插值型求积公式 牛顿 柯斯特 Newton Cotes 求积公式 定义 牛顿 柯斯特 Newton Cotes 求积公式是插值型求积公式的特殊形式 在插值求积公式 baf x dx baP x dx k 0nAkf xk a b
  • Stall Reservations POJ - 3190

    这道题 是学长给我们布置的学习用的题目 重在给我们讲解了什么是优先队列以及其对应的贪心问题 好了 先送上 中文翻译过的题意 手动 滑稽 Oh those picky N 1 lt N lt 50 000 cows They are so p
  • Armbian5.9.0如何安装docker及部署可视化portainer

    安装 docker 通过 ssh 进去 Armbian 系统后 输入下面代码 按提示输入y 等待安装完成即可 apt install docker io 如何查看 docker 是否安装成果 输入命令 docker 可出现docker帮助内
  • MySQL常用命令用法总结

    原文 http www jb51 net article 22110 htm 一 启动与退出 1 进入MySQL 启动MySQL Command Line Client MySQL的DOS界面 直接输入安装时的密码即可 此时的提示符是 my
  • Win 10系统无法连接蓝牙耳机问题

    问题描述 本人刚入手的笔记本电脑 设置里面也有 蓝牙 的开关 由于处于实验室的环境不能开外放 有线耳机显得很不舒服 于是本人兴奋地拿起平时用的蓝牙耳机 想和电脑连起来 但是打开蓝牙开关之后 遇到了下面的情况 打开添加完设备之后 不仅仅是本人
  • springboot2.0学习笔记 自定义JSON序列化程序和反序列化器

    如果使用jackson序列化和反序列化json数据 则可能需要编写 自己JsonSerializer和JsonDeserializer的类 Spring提供了一个替代方案 JsonComponent创建注释 直接注册spring bean容
  • SecureCRT 64位 破解版v8.1.4

    http www xue51 com soft 1510 html xzdz securecrt 破解版是一款支持SSH1和SSH2的终端仿真程序 这个程序能够在windows系统中登陆UNIX或Linux的服务器主机并且还能进行管理设置
  • 全网最细的SpringBoot3系列教程

    1 开发第个Spring Boot应用 创建POM 因为是3 0 0 M1版本 是程碑版本 不是正式发布版 需要从Spring提的Maven仓库中才能下载到3 0 0 M1版本的依赖包 需要在pom xml件中单独指定仓库地址 如果使的是正
  • 安卓真机调试安装失败Session ‘app‘: Installation did not succeed. The application could not be installed: IN:

    Session app Installation did not succeed The application could not be installed INSTALL FAILED TEST ONLY 解决方案 在gradle pr
  • 自定义Looper/Handler模型 线程wait/notify版本 非poll版本

    循环 public static class Looper final static ThreadLocal
  • PS证件照换底色

    原图 1 本教程采用photoshop CS5制作 其它版本基本通用 先在PS中打开原图 如下图所示 2 右键单击背景图层 在弹出的菜单中选择 复制图层 如下图所示 3 接着会弹出 复制图层 对话框 直接按确定即可 如下图所示 4 单击选中
  • pymysql的使用

    pymysql是从Python连接到MySQL数据库服务器的接口 其官方文档为 https pymysql readthedocs io en latest 安装 pip install pymysql 对于数据库的操作 我们一般是这样的操
  • 正在开发应用于Maxthon、TT等多页面浏览器的页面模式

    经过大量的用户调查 我们发现 有不少朋友使用了Maxthon 腾讯TT 世界之窗等基于IE的多页面浏览器使用WEBCHAT 而这种模式下弹出窗口将变成一个新页面 用起来不方便
  • 如何查看支付宝旗下的天弘基金一共有多少只?分别是什么?

    如何查看支付宝旗下的天弘基金一共有多少只 分别是什么 2020年 股市风格突变 相对股市个股的跌宕起伏 基金的收益可谓一枝独秀 下面我们将对基金进行研究 看看我们可以获取数据能否到什么程度 利用tushare的数据接口就可以获取基金的名称
  • 排序类算法

    文章目录 利用vector进行排序 数字类元素 字符串类元素 利用其他STL容器排序 map set priority queue 利用vector进行排序 数字类元素 每个元素一般包含多个条件 利用lambda编写特定排序条件 用sort
  • 转:Ogre TerrainGroup地形赏析

    转 Ogre TerrainGroup地形赏析 1 1 参考 http www ogre3d org tikiwiki tiki index php page Ogre Terrain System http www ogre3d org