[翻译]XNA 3.0 Game Programming Recipes之seven

PS:自己翻译的,转载请著明出处
                                                                2-11.创建一个实时相机依赖于优化自适应网格(漫游)地形
问题
    虽然您可以使用一个四叉树的机制中所如2-10节,以确保XNA使得只有那些地形的补丁是相机看不到的,这种做法是并非最佳。远离相机的补丁仍然作出同样的与接近于相机的补丁一样的高详细绘制。在屏幕上一些三角形正被绘制,甚至占用不到单个像素。
    因此,你想要使用level-Of-Detail(LOD)方法,地形远离摄象机的那一部分使用较少的细节。当您提供您的使用不同的地形的详细程度,你会发现在这些边界之间的会出现裂缝。
解决方案
    这节向您介绍了一个强有力的技术让您从两个三角形生成您的整个地形。在每个迭代算法,计算出每个三角形到相机的距离和自行决定是否应该再分成两个三角形。 最后,结束了一个网格的优化调整,以适应目前的摄像机的位置。
    这地形网格每一次需要更新的时候,摄象机的位置和旋转就正在被改变。为了保持最佳的新相机的情况信息,每个三角形还需要能够决定它是否应该与旁边的合并,再一次以减少详细度级别。
如何工作的
     这节的目标是从两个三角形覆盖了整个地形和创造每个三角形算法自行决定是否应该分拆或合,这些并取决于目前摄像头的视阈。
    你需要找到一个三角布局可以很容易的分裂和合并,然后才能真正开始。在节中涉及的地形非常远,我一直在使用一个固定的网格quadbased网。如果您分裂两个三角形的一个四叉树,以增加细节,每两个三角形的四叉树将需要分为八个三角形的四个方格,如图2-14右边所示,添加6个三角形是十分大的一步。

       此外,将可以看见不同的详细程度两个方格之间的裂缝将被看见,如图2-15所示。这个图象如2-14图右边所示。您需要做一些缝合修补裂缝等,这是既不容易,也不干净。

       因此,这节会使用一种完全不同的三角布局。代替perquad,您将每三角工作。任何时候一个三角形需要增加它的细节程度,很容易分这个三角形为两个,如2-16图从左到右。从两个到四个三角形你需要增加细节,这是一个更需要控制的步骤。
       此外,这不会造成分裂裂缝地形。在某些情况下,你可以保持分裂三角形这样不会产生裂缝的地形。图2-17展示三个以上的安全分裂。

       尽管,图2-17的右边显示分裂三角形B到C和D增加了一个裂缝在你的地形上。图2-18显示3D裂缝。

       幸运的是,你可以避免这些。当分裂三角形时避免裂缝产生,你应该做下面的事
    1。确保父三角形已经被分裂了。
    2。同样分裂三角形同样大小的形状。
    图2-19显示这个方法如果你想分裂三角形A。在上面左边的图形,这个虚线表明你想分裂。结合这里的第二条规则,三角共享相同的长度的边(三角B)应被第一个分割,如上右的图象图的虚线所示。结合第一规则,尽管,在三角形B分裂之前,首先要分裂三角形的父三角形(三角形D)为三角形B和C,如中左图象所示。在D三角形被分裂之前,尽管,你需要分裂三角形E。幸运的是,三角形E没有父三角形,所以你现在可以安全的分裂A,如底右图象所示。
   如此多的分裂。不管摄象机怎么移动地形保持最佳(意思是尽可能用少数的三角形),除此之外你的三角形应该把它们合并起来。当两个三角形A和B合成一个,三角形的两面,C和D,应该被合并成一个为了避免裂缝的产生。图2-20所示
初始化
    让我们转化一些代码。你开始载入含高度的图片,并建立一个VertexBuffer基于此图片,如5-8节。在这节,双方的长度和宽度的图片必须是(a power of2 +1 ).不管你是建立这尺寸的高度图片还是开始于这种大小的高度图片都是a power of 2和复制最后的行和列。保持重点,这节希望你有一个大小的高度图(power of 2+1).尽管,你在这节同样可以找到LoadHeightDataAndCopyLastRowAndColumn方法.
     添节这段代码到你的LoadContent类中,这个类载入了图象和建立了相应的VertexBuffer,如5-8节的例子:
1 Texture2D heightMap=Content.Load<Texture2D>("heightmap");
2 heightData=LoadHeightData(heightMap);
3 MyVertexDeclaration=new VertexDeclaration(device,VertexPositionNormalTexture.VertexElements);
4 VertexPositionNormalTexture[] terrainVertices=CreateTerrainVertices();
5 int[] terrainIndices=CreateTerrainIndices();
6 terrainVertices=GenerateNormalsForTriangleStrip(terrainVertices,terrainIndices);
7 terrainVertexBuffer=new VertexBuffer(device,VertexPositionNormalTexture.SizeInBytes*terrainVertices.Length,BufferUsage.WriteOnly);
8 terrainVertexBuffer.SetData(terrainVertices);
三角形的类
    你将会创建一个三角形的类,它自己可以定义所有的决定和行动。给你的工程添加一个新类,在你的解决方案右击你的工程的然后选择添加>新项目。在对话框里选择类型,输入文件名Triangle.cs。
    你得到的几乎是一个空的文件,你首先应该添加一些变量存储每一个三角形。这些变数储存环节的三个相邻的,子三角形 ,和父三角形:
1 Triangle lNeigh;
2 Triangle rNeigh;
3 Triangle bNeigh;
4 Triangle parent;
5 Triangle lChild;
6 Triangle rChild;
  
      你需要跟踪一些别的变量:
1 Vector3 lPos;
2 Vector3 centerPos;
3 int tInd;
4 int lInd;
5 int rInd;
6 public bool split=false;
7 public bool addedToMergeList=false;
     这2个Vector3包含3D的位置,三角形的左点和中心点,这个三角型需要被测定是否值得在分裂成小的三角形。该指数将被需要当三角形应被绘制时。该split布尔型将表明三角是否正在分裂,addedToMergeList布尔型将只需要确保对同一个三角形进行2次计算。
     在构造中其中一些变量将已确定,它们被调用当你创建一个新的三角型时:
 1 public Triangle(Triangle parent,Vector2 tPoint,Vector2 lPoint,Vector2 rPoint,float[,] heightData)
 2 {
 3     int resolution=heightData.GetLength(0);
 4     tInd=(int)(tPoint.X+tPoint.Y*resolution);
 5     iInd=(int)(lPoint.X+lPoint.Y*resolution);
 6     lInd=(int)(rPoint.X+rPoint.Y*resolution);
 7     lPos=new Vector3(lPoint.X,heightdata[(int)lPoint.X,(int)lPoint.Y],-lPoint.Y);
 8     Vector2 center=(lPoint+rPoint)/2;
 9     centerPos=new Vector3(center.X,heightData[(int)center.X,(int)center.Y],-center.Y);
10     this.parent=parent;
11     if(Vector2.Distance(lPoint,tPoint)>1)
12     {
13         lChild=new Triangle(this,center,tPoint,lPoint,heightData);
14         rChild=new Triangle(this,center,rPoint,tPoint,heightData);
15     }
16 }
    正如你所见,当建立一个新的Triangle对象时,你需要指定它的父节点,和它上左,上右的点。此外,知道三角形的结构将被用来呈现一副地图,这些点中的每一个将是一个3D位置。因此,当创建一个新的三角形对象时,你同样要指定一个2D数组包含整个地图的高度值。这是首次使用数组找到指数的三个角然后三维位置相应的左上角和中心点。
    最后,保存这些环节到父三角形和检测当前三角形是否会被分裂。如果三角形够大,你可以建立两个子三角形保存这些环节。注意的是中心点是两个三角形上面的点。具体参看2-21节。定义两个基础的三角形
   随着三角形类的一般结构被定义,你已经定义了两个基本的三角形,覆盖了你整个地形。将要开始绘制这两个三角形在第一祯。然后,Update方法每一次被调用,你将决定是否分裂在前面祯中的三角形。因此,你需要跟踪名单中的三角形哪些需要在当前绘制。添加这些变量在你的代码的顶部:
1 List<Triangle> triangleList;
    让我们移动到LoadContent方法的后面,这里你将添加一个三角形的结构。首先你先定义一个解决方案的变量,这个变量做为侦错变量。一般来说,你想绘制和你图象高度一样大小的地形,但是侦错的时候,它可以证明是有用的仅使用这图象的一段。虽然图象附带代码 本节分派1025*1025,现在你将只使用了33*33。
    这几行确定了左三角:
1 int terrainSize=32;
2 Triangle leftTriangle=new Triangle(null,new Vector2(0,0),new Vector2(terrainSize,0),new Vector(0,terrainSize),heightData);
   该基础三角形没有父三角形。至于第二,第三和第四的论述,你通过三角形的三个角点,相对应的角落点您的整个地形。最后你传递2D数组包含高度值。
   请注意,当这行将被执行,Trianle类的构造将被调用。这意味着,在设定时,这个三角形将自己创建两个子三角形,这在他们反过来将创建两个子三角形。这一进程仍在继续,直到所有的三角形的大小,两个高度地图点已经建立。
   右边的三角形用同样的方法也可以被精确定义,仅仅是角度位置不同。
1 Triangle rightTriangle=new Traingle(null,new Vector2(terrainSize,terrainSize),new Vector2(0,terrainSize),new Vector2(terrainSize,0),heightData);
添加环节
   所有的行动和决定都将会在Trianle类中执行,每一个三角形都会与父三角形,邻近的三角形,子三角形相连。因此,下一步是添加这些环节。
   每Triangle已经储存的环节,其父和子的三角形,但它仍然需要知道的彼此联系它的三个邻居。诀窍是, Triangle知道它的三个临近可以找出三个邻近的子三角形。当开始前, 这一机制将自动计算所有相邻的三角形在你的结构里。 开始加入这一方法的到Triangle类中:
1 public void AddNeighs(Triangle lNeight,Triangle rNeight,Triangle bNeigh)
2 {
3    this.lNeigh=lNeigh;
4    this.rNeigh=rNeigh;
5    this.bNeigh=bNeigh;
6 }
     该Triangle希望你通过链接到它的三个邻近三角形,它们被储存。接下来,在情况下的三角形有子三角形,你想弄清楚邻近的子三角形,使三角形您可以通过调用子三角形,从而通过正确的邻近子三角形,直到所有Trianles在整个结构存储他们的链接三个邻近的三角形
 1 if(lChild!=null)
 2 {
 3     Triangle bNeighRightChild=null;
 4     Triangle bNeighLeftChild=null;
 5     Triangle lNeighRightChild=null;
 6     Triangle rNeighLeftChild=null;
 7     if(bNeigh!=null)
 8     {
 9         bNeighLeftChild=bNeigh.lChild;
10         bNeighRightChild=bNeigh.rChild;
11     }    
12     if(lNeight!=null)
13        lNeighRightChild=lNeigh,rChild;
14     if(rNeight!=null)
15        rNeighLeftChild=rNeigh,lChild;
16     lChild.AddNeighs(rChild,bNeighRightChild,lNeighRightChild);
17     rChild.AddNeighs(bNeighLeftChild,lChild,rNeighLeftChild);
18 }
     所有您需要做的是启动这一进程的调用这个方法的两个基础三角形。 将此代码添加到结束的LoadContent方法
1 leftTriangle.AddNeighs(null,null,rightTriangle);
2 rightTriangle.AddNeighs(null,null,leftTriangle);
    每个基础三角形是唯一的其他的临近三角形。这两个简单的要求将传播通过您的整个结构,直到所有Trianle已储存的正确链,这个连接连接到他们的三个邻近三角形。
移动:The triangleList和IndexBuffer

posted on 2009-07-19 10:21  一盘散沙  阅读(309)  评论(0编辑  收藏  举报

导航