2011-05-09 39 views
3

我們正在製作一個運行在Xbox 360上的學校的3D遊戲。我們有一個巨大的關卡,它由大約100個零件組成,每個零件由許多網格組成,很多頂點。我們有一個自定義着色器,通過爲你周圍的等級着色,可以在任何時候發出類似ping效果。我們還有一個3D迷你地圖,因爲遊戲在太空中,你可以在任何方向定向。所以當我們繪製關卡時,我們必須每幀繪製4次,一個繪製主視圖端口,一個繪製主視圖端口中的ping,一個繪製迷你地圖中的水平,一個繪製水平在迷你地圖中的ping。它在一臺快速電腦上每秒運行60幀,但在Xbox上只能運行20幀。我們已經關閉了繪製我們背後的東西,你看不到它,它幫助了一些,但我們仍然需要它加快。通過製作一個網格優化繪製3D模型

這裏是只是出了主視口的Ping水平的主要拉...

//draw the main level in a regular way 
foreach (LevelObject part in levelData.LevelParts) 
{ 
    partBounds.Center = part.position; 
    if (viewFrustum.Intersects(partBounds)) 
    { 
     //Rotate X 
     Matrix worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(part.Xrotate)); 
     //Rotate Z 
     worldMatrix *= Matrix.CreateRotationZ(MathHelper.ToRadians(part.Zrotate)); 
     //Rotate Y 
     worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(part.Yrotate)); 

     worldMatrix *= Matrix.CreateWorld(part.position, Vector3.Forward, Vector3.Up); 
     Model Object = levelModels[part.modelName]; 

     //set in the gamers viewport 
     foreach (ModelMesh mesh in Object.Meshes) 
     { 
      foreach (BasicEffect effect in mesh.Effects) 
      { 
       //effect.EnableDefaultLighting(); 
       effect.LightingEnabled = true; 
       effect.AmbientLightColor = new Vector3(0.09f, 0.15f, 0.215f); 
       effect.DirectionalLight0.DiffuseColor = new Vector3(0.3f, 0.3f, 0.3f); 
       effect.DirectionalLight0.Direction = viewMatrix.Forward; 
       effect.DirectionalLight0.Enabled = true; 
       effect.DirectionalLight1.DiffuseColor = new Vector3(0.05f, 0.085f, 0.25f); 
       effect.DirectionalLight1.Direction = viewMatrix.Down; 
       effect.DirectionalLight1.Enabled = true; 

       effect.PreferPerPixelLighting = true; 
       effect.World = worldMatrix; 
       effect.View = viewMatrix; 
       effect.Projection = projectionMatrix; 
       mesh.Draw(); 
      } 
     } 
    } 
} 

所以,如果在攪拌機我做的水平部分只是一個目那就要少做循環,我不確定這是否會讓它畫得更快。我需要提高繪畫性能的任何想法嗎?這款遊戲是分屏式的,最多可以有4名玩家,這會使等級提高4倍。

以下是完整的繪製函數

public override void Draw(GameTime gameTime) 
{ 
    /* NORMAL VIEW */ 

    //set viewport for everyone 
    for (int i = 0; i < SignedInGamer.SignedInGamers.Count; i++) 
    { 
     GraphicsDevice.Viewport = Camera.gameScreenViewPorts[SignedInGamer.SignedInGamers[i]]; 
     Matrix viewMatrix = Camera.viewMatrix[SignedInGamer.SignedInGamers[i]]; 
     Matrix projectionMatrix = Camera.projectionMatrix[SignedInGamer.SignedInGamers[i]]; 

     GraphicsDevice.RasterizerState = RasterizerState.CullNone; 
     GraphicsDevice.BlendState = BlendState.Opaque; 

     //view frustrum object for culling 
     BoundingFrustum viewFrustum = new BoundingFrustum(viewMatrix * projectionMatrix); 
     BoundingSphere partBounds = new BoundingSphere(); 
     partBounds.Radius = levelData.scale; 

     //draw the main level in a regular way 
     foreach (LevelObject part in levelData.LevelParts) 
     { 
      partBounds.Center = part.position; 
      if (viewFrustum.Intersects(partBounds)) 
      { 
       //Rotate X 
       Matrix worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(part.Xrotate)); 
       //Rotate Z 
       worldMatrix *= Matrix.CreateRotationZ(MathHelper.ToRadians(part.Zrotate)); 
       //Rotate Y 
       worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(part.Yrotate)); 

       worldMatrix *= Matrix.CreateWorld(part.position, Vector3.Forward, Vector3.Up); 
       Model Object = levelModels[part.modelName]; 

       //set in the gamers viewport 
       foreach (ModelMesh mesh in Object.Meshes) 
       { 
        foreach (BasicEffect effect in mesh.Effects) 
        { 
         //effect.EnableDefaultLighting(); 
         effect.LightingEnabled = true; 
         effect.AmbientLightColor = new Vector3(0.09f, 0.15f, 0.215f); 
         effect.DirectionalLight0.DiffuseColor = new Vector3(0.3f, 0.3f, 0.3f); 
         effect.DirectionalLight0.Direction = viewMatrix.Forward; 
         effect.DirectionalLight0.Enabled = true; 
         effect.DirectionalLight1.DiffuseColor = new Vector3(0.05f, 0.085f, 0.25f); 
         effect.DirectionalLight1.Direction = viewMatrix.Down; 
         effect.DirectionalLight1.Enabled = true; 

         effect.PreferPerPixelLighting = true; 
         effect.World = worldMatrix; 
         effect.View = viewMatrix; 
         effect.Projection = projectionMatrix; 
         mesh.Draw(); 
        } 
       } 
      } 
     } 

     /* PING VIEW */ 

     List<Vector3> pingPos = new List<Vector3>(); 
     List<Vector4> pingColor = new List<Vector4>(); 
     List<float> pingRange = new List<float>(); 

     for (int a = 0; a < Game.Components.Count; a++) 
     { 
      if (Game.Components[a] is Ship) 
      { 
       pingPos.Add(((Ship)Game.Components[a]).worldMatrix.Translation); 
       pingColor.Add(new Vector4(
        ((Ship)Game.Components[a]).playerColor.R, 
        ((Ship)Game.Components[a]).playerColor.G, 
        ((Ship)Game.Components[a]).playerColor.B, 
        1)); 
       pingRange.Add(((Ship)Game.Components[a]).pingRange * (levelData.scale * 2.0f)); 
      } 
     } 


     if (pingPos.Count() > 0) 
     { 

      //apply ping lighting stuffs here 
      pingTest.Parameters["PingPos"].SetValue(pingPos.ToArray()); 
      pingTest.Parameters["PingPosCount"].SetValue(pingPos.Count()); 
      pingTest.Parameters["PingColor"].SetValue(pingColor.ToArray()); 
      pingTest.Parameters["PingColorCount"].SetValue(pingColor.Count()); 
      pingTest.Parameters["PingRange"].SetValue(pingRange.ToArray()); 
      pingTest.Parameters["PingRangeCount"].SetValue(pingRange.Count()); 
     } 

     foreach (LevelObject part in levelData.LevelParts) 
     { 
      partBounds.Center = part.position; 
      if (viewFrustum.Intersects(partBounds)) 
      { 
       //Rotate X 
       Matrix worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(part.Xrotate)); 
       //Rotate Z 
       worldMatrix *= Matrix.CreateRotationZ(MathHelper.ToRadians(part.Zrotate)); 
       //Rotate Y 
       worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(part.Yrotate)); 

       worldMatrix *= Matrix.CreateWorld(part.position, Vector3.Forward, Vector3.Up); 
       Model Object = levelModels[part.modelName]; 

       foreach (ModelMesh mesh in Object.Meshes) 
       { 
        //ok, this is going to be kind of weird, and there's got to be a cleaner 
        //or better way to do this. 
        List<Effect> backup = new List<Effect>(); 

        foreach (ModelMeshPart meshpart in mesh.MeshParts) 
        { 
         backup.Add(meshpart.Effect); 
         meshpart.Effect = pingTest; 
         meshpart.Effect.Parameters["World"].SetValue(worldMatrix * mesh.ParentBone.Transform); 
         meshpart.Effect.Parameters["View"].SetValue(viewMatrix); 
         meshpart.Effect.Parameters["Projection"].SetValue(projectionMatrix); 

         //Matrix worldInverseTransposeMatrix = Matrix.Transpose(Matrix.Invert(mesh.ParentBone.Transform * world)); 
         //pingTest.Parameters["WorldInverseTranspose"].SetValue(worldInverseTransposeMatrix); 
        } 

        mesh.Draw(); 

        //reset the basic effect crap 
        foreach (ModelMeshPart meshpart in mesh.MeshParts) 
        { 
         meshpart.Effect = backup.First(); 
         backup.RemoveAt(0); 
        } 
        // 
       } 
      } 
     } 

     //Undo the weird things this shader does 
     GraphicsDevice.BlendState = BlendState.Opaque; 
     GraphicsDevice.DepthStencilState = DepthStencilState.Default; 

     /* MINIMAP VIEW */ 
     GraphicsDevice.Viewport = Camera.mapViewports[SignedInGamer.SignedInGamers[i]]; 
     viewMatrix = Camera.mapViewMatrix[SignedInGamer.SignedInGamers[i]]; 
     projectionMatrix = Camera.mapProjectionMatrix[SignedInGamer.SignedInGamers[i]]; 

     GraphicsDevice.RasterizerState = RasterizerState.CullNone; 
     GraphicsDevice.BlendState = BlendState.Opaque; 

     //view frustum for the map 
     BoundingFrustum mapFrustum = new BoundingFrustum(viewMatrix * projectionMatrix); 

     //draw the main level in a regular way 
     foreach (LevelObject part in levelData.LevelParts) 
     { 
      partBounds.Center = part.position; 
      if (mapFrustum.Intersects(partBounds)) 
      { 
       //Rotate X 
       Matrix worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(part.Xrotate)); 
       //Rotate Z 
       worldMatrix *= Matrix.CreateRotationZ(MathHelper.ToRadians(part.Zrotate)); 
       //Rotate Y 
       worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(part.Yrotate)); 

       worldMatrix *= Matrix.CreateWorld(part.position, Vector3.Forward, Vector3.Up); 
       Model Object = levelModels[part.modelName]; 

       //set in the gamers viewport 
       foreach (ModelMesh mesh in Object.Meshes) 
       { 
        foreach (BasicEffect effect in mesh.Effects) 
        { 
         //effect.EnableDefaultLighting(); 
         effect.LightingEnabled = true; 

         if (match(part.position/15.0f)) 
          effect.AmbientLightColor = new Vector3(1.0f, 0.30f, 0.43f); 
         else 
          effect.AmbientLightColor = new Vector3(0.18f, 0.30f, 0.43f); 
         //effect.DirectionalLight0.DiffuseColor = new Vector3(0.6f, 0.6f, 0.6f); 
         effect.DirectionalLight0.DiffuseColor = new Vector3(0.6f, 0.6f, 0.6f); 
         effect.DirectionalLight0.Direction = viewMatrix.Forward; 
         effect.DirectionalLight0.Enabled = true; 
         //effect.DirectionalLight1.DiffuseColor = new Vector3(0.1f, 0.17f, 0.5f); 
         effect.DirectionalLight1.DiffuseColor = new Vector3(0.1f, 0.17f, 0.5f); 
         effect.DirectionalLight1.Direction = viewMatrix.Down; 
         effect.DirectionalLight1.Enabled = true; 

         effect.PreferPerPixelLighting = true; 
         effect.World = worldMatrix; 
         effect.View = viewMatrix; 
         effect.Projection = projectionMatrix; 
         mesh.Draw(); 
        } 

       } 
      } 
     } 

     base.Draw(gameTime); 
     return; 

     if (pingPos.Count() > 0) 
     { 
      //apply ping lighting stuffs here 
      pingTest.Parameters["PingPos"].SetValue(pingPos.ToArray()); 
      pingTest.Parameters["PingPosCount"].SetValue(pingPos.Count()); 
      pingTest.Parameters["PingColor"].SetValue(pingColor.ToArray()); 
      pingTest.Parameters["PingColorCount"].SetValue(pingColor.Count()); 
      pingTest.Parameters["PingRange"].SetValue(pingRange.ToArray()); 
      pingTest.Parameters["PingRangeCount"].SetValue(pingRange.Count()); 
     } 

      foreach (LevelObject part in levelData.LevelParts) 
      { 
       partBounds.Center = part.position; 
       if (mapFrustum.Intersects(partBounds)) 
       { 
        //Rotate X 
        Matrix worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(part.Xrotate)); 
        //Rotate Z 
        worldMatrix *= Matrix.CreateRotationZ(MathHelper.ToRadians(part.Zrotate)); 
        //Rotate Y 
        worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(part.Yrotate)); 

        worldMatrix *= Matrix.CreateWorld(part.position, Vector3.Forward, Vector3.Up); 
        Model Object = levelModels[part.modelName]; 

        foreach (ModelMesh mesh in Object.Meshes) 
        { 
         //ok, this is going to be kind of weird, and there's got to be a cleaner 
         //or better way to do this. 
         List<Effect> backup = new List<Effect>(); 

         foreach (ModelMeshPart meshpart in mesh.MeshParts) 
         { 
          backup.Add(meshpart.Effect); 
          meshpart.Effect = pingTest; 
          meshpart.Effect.Parameters["World"].SetValue(worldMatrix * mesh.ParentBone.Transform); 
          meshpart.Effect.Parameters["View"].SetValue(viewMatrix); 
          meshpart.Effect.Parameters["Projection"].SetValue(projectionMatrix); 

          //Matrix worldInverseTransposeMatrix = Matrix.Transpose(Matrix.Invert(mesh.ParentBone.Transform * world)); 
          //pingTest.Parameters["WorldInverseTranspose"].SetValue(worldInverseTransposeMatrix); 



         } 

         mesh.Draw(); 

         //reset the basic effect crap 
         foreach (ModelMeshPart meshpart in mesh.MeshParts) 
         { 
          meshpart.Effect = backup.First(); 
          backup.RemoveAt(0); 
         } 
         //*/ 
        } 
       } 
      } 

     //} 
     //Undo the weird things this shader does 
     GraphicsDevice.BlendState = BlendState.Opaque; 
     GraphicsDevice.DepthStencilState = DepthStencilState.Default; 

     base.Draw(gameTime); 
    } 

} 

關於該垃圾收集

因此,通過使用這種方法設置載體像effect.DirectionalLight0.DiffuseColor ...

effect.DirectionalLight0.DiffuseColor.X = 0.3f; 
effect.DirectionalLight0.DiffuseColor.Y = 0.3f; 
effect.DirectionalLight0.DiffuseColor.Z = 0.3f; 

而不是這種方法....

effect.DirectionalLight0.DiffuseColor = new Vector3(.3, .3, .3); 

我分配較少的內存垃圾收集回暖,但一旦Draw()函數完成被稱爲不將其添加所有的垃圾堆放,以便收集器可以把它撿起來幾乎瞬間?我意識到它爲收藏家增加了一些額外的工作,但它不應該增加那麼多的權利?

+0

使用在未還原的地圖-主? – 2011-05-09 05:14:41

回答

2

速度問題並不一定來自您的想法。 C#在視頻遊戲中的嚴重問題是,它的容易做錯事情,因爲他們看起來很漂亮

例如:

effect.AmbientLightColor = new Vector3(0.09f, 0.15f, 0.215f); 

這是令人難以置信的相當。你在那裏分配一個Vector3。還有2人。 但是,嘿,你在雙foreach循環! 以前的effect.AmbientLightColor去哪了?那麼,它會收集垃圾。這是每幀分配和垃圾收集的lot

相反,你應該使用一些時間,但更高效:

effect.AmbientLightColor.X = 0.09f; 
effect.AmbientLightColor.Y = 0.15f; 
effect.AmbientLightColor.Z = 0.215f; 

如果這樣做,處處,你實際上並不需要分配新的對象,你會看到一個相當性能提升。

黃金法則是總是避免撥款。不過,如果你提供了更多的代碼,我可以試着幫助更多,但我認爲這應該已經有所幫助。

+0

對於任何C#程序員來說,這實際上是一個很好的提示,因爲使用GC的程序員經常會在空間和時間上忘記內存的價值。 – 2011-05-09 11:51:03

+0

你和AShelly都有好主意,我會在幾分鐘內上學時解決這個問題。讓我發佈其餘的繪圖函數供您查看。 – Rickyman35 2011-05-09 19:52:43

+2

在你的例子中,如果有的話,應該真的有很小的差異。 Vector3是一個'struct',所以這裏唯一的開銷是構造函數調用和字段初始化,但是JIT可能會內聯。事實上,第一個是可取的,因爲第二個修改了一個令人不悅的結構,因爲它給出了意想不到的結果。可能有差異,但在假設之前進行測量。無論哪種方式,'struct'永遠不會獲得GCd(單獨作爲引用類型的一部分,但不會增加開銷),所以GC甚至不會在您的示例中發揮作用。 – JulianR 2011-05-09 21:40:38

1

我完全同意@ Heandel的回答。這裏還有一些除了分配之外的東西:

什麼是內部循環內的實際變量?似乎只有2個方向向量和3個矩陣呼叫的分配正在改變。因此,當效果最初添加到網格時(可能在加載時),而不是每個渲染幀一次,只能調用12行中的7行。

Model Object = levelModels[part.modelName];:這是做每個部分每個字符串的散列查找?爲什麼不在水平加載時只做一次,並將零件的模型引用存儲起來,而不僅僅是名稱?

而且您爲每個零件撥打3個電話給MathHelper.ToRadians。爲什麼不直接以弧度存儲旋轉?

+1

看到擴展的代碼之後:通常,仔細查看您正在執行的每一幀都不會改變每一幀的內容。 (搜索所有組件以查找所有船隻?)查找每幀每個對象多次執行的操作。 (檢查它是否在視野中?生成worldMatrix?)將所有這些東西移動到只需每次更改一次就完成一次的地方。 – AShelly 2011-05-09 20:41:57

1

一兩件事,吃了超過1/2的幀速率是mesh.Draw(); 是在錯誤的循環,你有

mesh.Draw(); 
} 
} 

應該

} 
mesh.Draw(); 
}