通过制作一个网格来优化绘制 3D 模型

发布于 2024-11-05 16:58:53 字数 15126 浏览 1 评论 0原文

我们正在为学校制作一款在 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();
            }
        }
    }
}

因此,如果在搅拌机中我将关卡部分制作为一个网格,则必须执行更少的循环,我不确定这是否会使它画得更快。我需要什么想法来大幅提高绘图性能?该游戏为分屏游戏,最多可容纳 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()函数,它不会将所有垃圾添加到堆栈中,以便收集器几乎可以立即拾取它吗?我意识到它为收集器增加了一些额外的工作,但它不应该增加那么多,对吗?

We are making a 3D game for school that runs on the Xbox 360. We have a huge level that consists of about 100 parts, each part consists of a lot of meshes which consists of a lot of vertices. We have a custom shader that will give off a ping like effect where ever you are in the level by shading the level around you. We also have a 3D mini map since the game is in space and you can be orientated in any direction. So when we draw the level we have to draw it 4 times per frame, one to draw in the main view port, one to draw the pings in the main view port, one to draw the level in the mini map, and one to draw the pings in the mini map. It runs a 60 frames per second on a fast PC but only at 20 on an xbox. We already turned off drawing the pieces behind us that you can not see and it helped some but we still need it to go faster.

Here is the main drawing for just the level with out the pings on the main view port...

//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();
            }
        }
    }
}

So if in blender I made the level part just one mesh it would have to do less loops, I'm not sure if that would make it draw faster. Any ideas I need to increase the drawing performance by a lot? This game is split screen and can have up to 4 players, which would increase the level draws by 4 times.

Here is the full draw function

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);
    }

} 

Question about the garbage collector

So by setting vectors like effect.DirectionalLight0.DiffuseColor using this method...

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

Instead of this method....

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

I allocates less memory for the garbage collector to pick up, but once the draw() function is done being called doesn't it add all the garbage to stack so the collector can pick it up almost instantly? I realize it adds a little extra work for the collector, but it shouldn't add that much right?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

2024-11-12 16:58:53

速度的问题并不一定来自于你的想法。 C# 在视频游戏中的严重问题是,它非常很容易做错事情,因为它们看起来很漂亮

例如:

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

那太漂亮了。漂亮。您在那里分配一个 Vector3 。还有另外 2 个人。
但是,嘿,您正处于一个双 foreach 循环中!
之前的effect.AmbientLightColor去哪儿了?好吧,它会被垃圾收集。每帧有大量的分配和垃圾回收。

相反,您应该使用更长但效率更高的东西:

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

如果您在实际上不需要分配新对象的任何地方都这样做,您将看到性能的显着提高。

黄金法则是始终避免分配。如果您提供更多代码,我可以尝试提供更多帮助,但我认为这应该已经有所帮助。

The issue of speed doesn't necessarily comes from what you think. The serious problem with C# in video games is that it's very easy to do wrong things because they look pretty.

For example :

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

That's incredibly pretty. You're allocating a Vector3 there. And 2 others.
But hey, you're in a double foreach loop!
Where did the previous effect.AmbientLightColor go ? Well, it's gonna get garbage collected. That's a lot of allocations and garbage collection per frame.

Instead you should use something longer but much more efficient:

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

If you do this everywhere where you don't actually need to allocate new objects, you will see a considerable performance increase.

A golden rule is always avoid allocations. I could try to help more if you provided more code, though, but I think this should help already.

雨后咖啡店 2024-11-12 16:58:53

我完全同意@Heandel的回答。除了分配之外,这里还有一些事情:

内部循环中实际上有什么变量?看来只有 2 个方向向量和 3 个矩阵调用的分配发生了变化。因此,当效果最初添加到网格时(可能在加载时),12 行中的 7 行只能调用一次,而不是每个渲染帧调用一次。

Model Object = levelModels[part.modelName]; :这是每帧对每个部分的字符串进行哈希查找吗?为什么不在水平加载时只执行一次此操作,并将模型引用与零件一起存储,而不仅仅是名称?

对于每个部分,您对 MathHelper.ToRadians 进行 3 次调用。为什么不一开始就以弧度存储旋转呢?

I completely agree with @Heandel's answer. Here are a few more things besides the allocation:

What is actually variable inside the inner loop? It appears that only the assignment of 2 direction vectors and 3 matrixes call are changing. So 7 out of the 12 lines could be called only once when the effect is initially added to the mesh (probably at load time), instead of once per render frame.

Model Object = levelModels[part.modelName]; : Is this doing a hash lookup of a string for each part every frame? Why not do this only once on level load, and store a Model reference with the part, instead of just a name?

And you make 3 calls to MathHelper.ToRadians for each part. Why not just store the rotations in radians to begin with?

明月松间行 2024-11-12 16:58:53

消耗超过 1/2 帧速率的一件事是 mesh.Draw();
是在错误的循环中,你

mesh.Draw();
}
}

应该是

}
mesh.Draw();
}

One thing that eating up over 1/2 your frame rate is the mesh.Draw();
is in the wrong loop, you have

mesh.Draw();
}
}

it should be

}
mesh.Draw();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文