Why does obstructed surfaces are rendering above the front surfaces?

60 views Asked by At

I have tried to create some asteroids for my 3d game on monogame framework, downloaded models from website and uploaded them to monogame through content manager. When I tried to render them into the world and make them spin, at some times there are surfaces that obviously positioned behind asteroid, but are being rendered above. bug example link to asteroid model link to used texture Including used code:

///////////////////////////////////////////////////////////////////
internal class Object3D
{
    protected Model model3d;

    protected Matrix position_matrix;
    protected Matrix rotation_matrix;
    protected Matrix reposition_matrix = Matrix.CreateRotationX(0f) *
                                         Matrix.CreateRotationY(0f) *
                                         Matrix.CreateRotationZ(0f);

    protected Vector3 position;
    protected Vector3 rotation;
    protected Vector3 rotation_speed = Vector3.Zero;
    protected Vector3 speed = Vector3.Zero;

    public Object3D(Vector3 start_position, Vector3 start_rotation)
    {
        setPosition(start_position);
        setRotation(start_rotation);
    }

    public Object3D(Vector3 start_position)
    {
        setPosition(start_position);
        setRotation(Vector3.Zero);
    }
    public Object3D()
    {
        setPosition(Vector3.Zero);
        setRotation(Vector3.Zero);
    }

    public void setRotation(Vector3 new_rotation)
    {
        rotation.X = new_rotation.X % 360;
        rotation.Y = new_rotation.Y % 360;
        rotation.Z = new_rotation.Z % 360;
        rotation_matrix = Matrix.CreateRotationX(MathHelper.ToRadians(new_rotation.X)) *
                          Matrix.CreateRotationY(MathHelper.ToRadians(new_rotation.Y)) *
                          Matrix.CreateRotationZ(MathHelper.ToRadians(new_rotation.Z));
    }

    public void setPosition(Vector3 new_position)
    {
        position = new_position;
        position_matrix = Matrix.CreateTranslation(new_position);
    }

    public void setSpeed(Vector3 new_speed) => speed = new_speed;

    public void setReposition(Matrix new_reposition) => reposition_matrix = new_reposition;

    public void setRotationSpeed(Vector3 new_rotation_speed) => rotation_speed = new_rotation_speed;

    public void setRotationSpeedX(float rotation_speed_x) => rotation_speed.X = rotation_speed_x % 360;

    public void setRotationSpeedY(float rotation_speed_y) => rotation_speed.Y = rotation_speed_y % 360;

    public void setRotationSpeedZ(float rotation_speed_z) => rotation_speed.Z = rotation_speed_z % 360;

    public void loadModel(ContentManager content, string name)
    {
        try
        {
            model3d = content.Load<Model>(name);
        }
        catch 
        {
            model3d = content.Load<Model>("Ship");
        }
    }

    public Matrix getRotationMatrix() => rotation_matrix;

    public Vector3 getRotation() => rotation;

    public Matrix getPositionMatrix() => position_matrix;

    public Vector3 getPosition() => position;

    public Vector3 getSpeed() => speed;

    public Vector3 getRotationSpeed() => rotation_speed;

    public Matrix getDrawMatrix(Matrix relative) => relative * reposition_matrix * rotation_matrix * position_matrix;

    public Model getModel() => model3d;

    public void move()
    {
        setPosition(position + speed);
        setRotation(rotation + rotation_speed);
    }

    public virtual void Update() { }
}
///////////////////////////////////////////////////////////////////
internal class Asteroid : Object3D
{
    public Asteroid() : base() { }

    public Asteroid(Vector3 start_position) : base(start_position) { }

    public override void Update()
    {
        move();
    }
}
///////////////////////////////////////////////////////////////////
internal class Camera
{
    private Matrix view;
    private Matrix projection;

    private Vector3 position;
    private Vector3 abs_position;
    private Vector3 target_position;
    private Vector3 up_vector;

    public Camera(Vector3 start_position, Vector3 start_target_position, GraphicsDeviceManager _graphics)
    {
        position = start_position;
        target_position = start_target_position;
        up_vector = Vector3.Up;
        view = Matrix.CreateLookAt(position, target_position, up_vector);
        projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
                                                        _graphics.PreferredBackBufferWidth / _graphics.PreferredBackBufferHeight,
                                                        0.1f,
                                                        200f);
        abs_position = position;
    }

    public Camera(Vector3 start_position, GraphicsDeviceManager _graphics)
    {
        position = start_position;
        target_position = Vector3.Zero;
        up_vector = Vector3.Up;
        view = Matrix.CreateLookAt(position, target_position, up_vector);
        projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
                                                        _graphics.PreferredBackBufferWidth / _graphics.PreferredBackBufferHeight,
                                                        0.1f,
                                                        200f);
        abs_position = position;
    }

    public Camera(GraphicsDeviceManager _graphics)
    {
        position = Vector3.Zero;
        target_position = Vector3.Zero;
        up_vector = Vector3.Up;
        view = Matrix.CreateLookAt(position, target_position, up_vector);
        projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
                                                        _graphics.PreferredBackBufferWidth / _graphics.PreferredBackBufferHeight,
                                                        0.1f,
                                                        200f);
        abs_position = position;
    }

    public void updateView() => view = Matrix.CreateLookAt(position, target_position, up_vector);

    public void setPosition(Vector3 new_position)
    {
        position = new_position;
        updateView();
    }

    public void setTargetPosition(Vector3 new_target_position)
    {
        target_position = new_target_position;
        updateView();
    }

    public void setUpVector(Vector3 new_up_vector)
    {
        up_vector = new_up_vector;
        updateView();
    }

    public Vector3 getPosition() => position;

    public Vector3 getAbsPosition() => abs_position;

    public Vector3 getTargetPosition() => target_position;

    public Matrix getView() => view;

    public Matrix getProjection() => projection;
}
///////////////////////////////////////////////////////////////////
public class Game1 : Game
{
    Matrix world;
    
    Ship ship;
    Camera camera;

    Asteroid[] asteroids;

    SpriteFont font;

    VertexBuffer vertexBuffer;
    VertexPositionColor[] vertices;
    BasicEffect effectLine;

    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void Initialize()
    {
        // TODO: Add your initialization logic here

        ship = new Ship();
        camera = new Camera(new Vector3(0f, 10f, 10f), ship.getPosition(), _graphics);
        world = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);

        vertices = new VertexPositionColor[8];
        vertices[0] = new VertexPositionColor(Vector3.Zero, Color.Yellow);
        vertices[1] = new VertexPositionColor(ship.getSpeed() * 5, Color.Yellow);

        vertices[2] = new VertexPositionColor(Vector3.Zero, Color.Red);
        vertices[3] = new VertexPositionColor(Vector3.UnitX * 10, Color.Red);

        vertices[4] = new VertexPositionColor(Vector3.Zero, Color.Green);
        vertices[5] = new VertexPositionColor(Vector3.UnitY * 10, Color.Green);

        vertices[6] = new VertexPositionColor(Vector3.Zero, Color.Purple);
        vertices[7] = new VertexPositionColor(Vector3.UnitZ * 10, Color.Purple);

        vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), vertices.Length, BufferUsage.None);
        vertexBuffer.SetData(vertices);

        effectLine = new BasicEffect(GraphicsDevice);
        effectLine.VertexColorEnabled = true;

        asteroids = new Asteroid[8];

        var distance = 100f;

        asteroids[0] = new Asteroid(new Vector3(distance, distance, distance));
        asteroids[1] = new Asteroid(new Vector3(distance, distance, -distance));
        asteroids[2] = new Asteroid(new Vector3(-distance, distance, -distance));
        asteroids[3] = new Asteroid(new Vector3(-distance, distance, distance));

        asteroids[4] = new Asteroid(new Vector3(distance, -distance, distance));
        asteroids[5] = new Asteroid(new Vector3(distance, -distance, -distance));
        asteroids[6] = new Asteroid(new Vector3(-distance, -distance, -distance));
        asteroids[7] = new Asteroid(new Vector3(-distance, -distance, distance));

        base.Initialize();
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);

        ship.loadModel(Content, "Ship");
        ship.setReposition(Matrix.CreateRotationX(MathHelper.ToRadians(-90f)) * Matrix.CreateRotationY(MathHelper.ToRadians(180f)));
        font = Content.Load<SpriteFont>("File");

        for(int i = 0; i < asteroids.Length / 2; i++)
        {
            asteroids[i].loadModel(Content, "Asteroid1"); //Change to asteroid model
        }

        for (int i = asteroids.Length / 2; i < asteroids.Length; i++)
        {
            asteroids[i].loadModel(Content, "Asteroid1"); //Change to asteroid model
        }

        var rand = new Random();
        foreach (Asteroid asteroid in asteroids) 
            asteroid.setRotationSpeed(new Vector3((float)rand.NextDouble() * 3, (float)rand.NextDouble() * 3, (float)rand.NextDouble() * 3));
        // TODO: use this.Content to load your game content here

    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        // TODO: Add your update logic here

        ship.Update();

        if (Keyboard.GetState().IsKeyUp(Keys.P))
        {
            foreach (Asteroid asteroid in asteroids)
                asteroid.Update();
        }
        
        camera.setTargetPosition(ship.getPosition());
        camera.setUpVector(Vector3.Transform(Vector3.Up, ship.getRotationMatrix()));
        camera.setPosition(Vector3.Transform(camera.getAbsPosition(), ship.getRotationMatrix() * ship.getPositionMatrix()));

        vertices[1].Position = ship.getSpeed() * 5;

        vertexBuffer.SetData(vertices);

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Blue);
        // TODO: Add your drawing code here
        _spriteBatch.Begin();
        _spriteBatch.DrawString(font, $"Velocity( X:{ship.getSpeed().X} Y:{ship.getSpeed().Y} Z:{ship.getSpeed().Z} )", new Vector2(10, 10), Color.Black);
        _spriteBatch.DrawString(font, $"Position( X:{ship.getPosition().X} Y:{ship.getPosition().Y} Z:{ship.getPosition().Z} )", new Vector2(10, 100), Color.Black);
        _spriteBatch.End();

        foreach (Asteroid asteroid in asteroids)
        {
            foreach (ModelMesh mesh in asteroid.getModel().Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = asteroid.getDrawMatrix(world);
                    effect.View = camera.getView();
                    effect.Projection = camera.getProjection();
                }
                mesh.Draw();
            }
        }

        foreach (ModelMesh mesh in ship.getModel().Meshes)
        {
            foreach(BasicEffect effect in mesh.Effects)
            {
                effect.World = ship.getDrawMatrix(world);
                effect.View = camera.getView();
                effect.Projection = camera.getProjection();
            }
            mesh.Draw();
        }

        effectLine.World = world * ship.getPositionMatrix();
        effectLine.View = camera.getView();
        effectLine.Projection = camera.getProjection();

        GraphicsDevice.SetVertexBuffer(vertexBuffer);
        foreach (EffectPass pass in effectLine.CurrentTechnique.Passes)
        {
            pass.Apply();

            GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
                (PrimitiveType.LineList, vertices, 0, 4);
        }

        base.Draw(gameTime);
    }
}

I used different texture for the asteroid model. I imported my model to blender and set the used texture to it, before that I set the texture size to 1024x1024. I don't know if this is a problem with my model or the inner workings and 3d model rendering of monogame. I will be thakful if someone can give an advice in this situation. In my program I've been using the .fbx model. I have tried several .fbx models but to no extent, they all have the same problem.

1

There are 1 answers

2
Rafloka On

This looks a lot like there is no depth testing taking place while rendering the meshes. Try that after your "_spriteBatch.End();":

    GraphicsDevice.DepthStencilState = new() {
        DepthBufferEnable = true,
        DepthBufferWriteEnable = true,
        DepthBufferFunction = CompareFunction.LessEqual
    };

If you use custom shaders (effects) you can also use something like:

pass P0 {
    ZFunc = LESSEQUAL;
    ZWriteEnable = true;
    ...
    ...
}

Another option is that the winding order of the model is the opposite of the rasterizer. For that you can test:

    GraphicsDevice.RasterizerState = new() {
        CullMode = CullMode.CullClockwiseFace
    };

and

    GraphicsDevice.RasterizerState = new() {
        CullMode = CullMode.CullCounterClockwiseFace
    };

Let me know if any of this helped!