通过制作一个网格来优化绘制 3D 模型
本文关键字:网格 优化 绘制 模型 3D 一个 | 更新日期: 2023-09-27 17:55:22
我们正在为学校制作一款在Xbox 360上运行的3D游戏。我们有一个巨大的关卡,由大约 100 个部分组成,每个部分由许多网格组成,这些网格由许多顶点组成。我们有一个自定义着色器,它会通过着色你周围的关卡来发出类似ping的效果。我们还有一个3D迷你地图,因为游戏在太空中,你可以朝任何方向定向。因此,当我们绘制关卡时,我们必须每帧绘制 4 次,一次在主视口绘制,一次在主视口绘制 ping,一次在小地图中绘制关卡,一次在小地图中绘制 ping。它在快速PC上每秒运行60帧,但在xbox上仅以20帧的速度运行。我们已经关闭了绘制您看不到的身后部分,它帮助了一些人,但我们仍然需要它走得更快。
这是关卡的主图,没有主视口上的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();
}
}
}
}
因此,如果在Blender中,我只制作了一个网格,那么它必须执行更少的循环,我不确定这是否会使其绘制得更快。我需要将绘图性能提高很多的想法吗?该游戏是分屏的,最多可以有 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);
}
}
关于垃圾回收器的问题
所以通过设置像效果这样的矢量。方向光0.漫反射颜色使用此方法...
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()函数,它不会将所有垃圾添加到堆栈中,以便收集器几乎可以立即拾取它吗?我意识到它为收藏家增加了一些额外的工作,但它不应该增加那么多,对吗?
速度的问题不一定来自你的想法。C#在视频游戏中的严重问题是,很容易做错事,因为它们看起来很漂亮。
例如:
effect.AmbientLightColor = new Vector3(0.09f, 0.15f, 0.215f);
这真是太漂亮了。你在那里分配了一个 Vector3。还有另外 2 个。但是,嘿,你处于双重循环中!前effect.AmbientLightColor
去哪儿了?好吧,它会被垃圾收集。这是每帧的大量分配和垃圾回收。
相反,你应该使用更长但更有效的东西:
effect.AmbientLightColor.X = 0.09f;
effect.AmbientLightColor.Y = 0.15f;
effect.AmbientLightColor.Z = 0.215f;
如果在实际上不需要分配新对象的所有位置执行此操作,则会看到性能的显着提高。
黄金法则始终是避免分配。不过,如果您提供更多代码,我可以尝试提供更多帮助,但我认为这应该已经有所帮助。
我完全同意@Heandel的回答。 除了分配之外,这里还有一些事情:
内循环内到底是什么变量? 似乎只有 2 个方向向量和 3 个矩阵调用的分配在变化。 因此,当效果最初添加到网格体时(可能在加载时),12 行中的 7 行只能调用一次,而不是每个渲染帧调用一次。
Model Object = levelModels[part.modelName];
: 这是在每帧对每个部分的字符串进行哈希查找吗? 为什么不在水平加载时只执行此操作一次,并将模型参考与零件一起存储,而不仅仅是名称?
您为每个部分拨打 3 次电话来MathHelper.ToRadians
。 为什么不一开始就以弧度存储旋转呢?
帧速率超过 1/2 的一件事是mesh.Draw();
在错误的循环中,你有
mesh.Draw();
}
}
它应该是
}
mesh.Draw();
}