Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange "fuzz" When Rendering Texture on Plane in XNA 4.0

Tags:

xna

I'm just getting started with XNA and I am sure I am missing something very simple. I have a quad plane I drew for the ground. On this plane I wrapped a 2D texture. The texture looks good up close, but as I move the camera around, I see a bunch of white artifacts all over the place. They disappear as I get close to them. I'm guessing this is a sampling problem or something similar, but I am stuck.

First, I created a QuadDrawer class to draw the plane for me. It's derived from DrawableGameComponent.

QuadDrawer:

class QuadDrawer : DrawableGameComponent
  {
    private string _textureName;
    private Texture2D _texture;
    private BasicEffect _effect;

    private float _size = 100;

    private VertexPositionNormalTexture[] _vertices;
    private int[] _indices;

    public QuadDrawer(Game game, float size, string textureName)
      : base(game)
    {
      this._size = size;
      this._textureName = textureName;
    }

    public override void Initialize()
    {
      BuildVertices();
      base.Initialize();
    }

    private void BuildVertices()
    {
      _vertices = new VertexPositionNormalTexture[4];
      _indices = new int[6];

      _vertices[0].Position = Vector3.Forward + Vector3.Left;
      _vertices[0].TextureCoordinate = new Vector2(0.0f, 1.0f);
      _vertices[1].Position = Vector3.Backward + Vector3.Left;
      _vertices[1].TextureCoordinate = new Vector2(0.0f, 0.0f);
      _vertices[2].Position = Vector3.Forward + Vector3.Right;
      _vertices[2].TextureCoordinate = new Vector2(1.0f, 1.0f);
      _vertices[3].Position = Vector3.Backward + Vector3.Right;
      _vertices[3].TextureCoordinate = new Vector2(1.0f, 0.0f);

      for (int i = 0; i < _vertices.Length; i++)
      {
        _vertices[i].Normal = Vector3.Up;
        _vertices[i].Position *= _size;
        _vertices[i].TextureCoordinate *= _size;
      }

      _indices[5] = 0; _indices[4] = 1; _indices[3] = 2;
      _indices[2] = 2; _indices[1] = 1; _indices[0] = 3;
    }

    protected override void LoadContent()
    {
      _texture = this.Game.Content.Load<Texture2D>(_textureName);
      _effect = new BasicEffect(this.GraphicsDevice);
      _effect.EnableDefaultLighting();
      _effect.PreferPerPixelLighting = true;
      _effect.SpecularColor = new Vector3(0.1f, 0.1f, 0.1f);

      _effect.World = Matrix.Identity;
      _effect.TextureEnabled = true;
      _effect.Texture = _texture;

      base.LoadContent();
    }

    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);
    }

    public override void Draw(GameTime gameTime)
    {
      MyGame game = this.Game as MyGame;

      GraphicsDevice.SamplerStates[0] = SamplerState.AnisotropicWrap;
      GraphicsDevice.DepthStencilState = DepthStencilState.Default;

      _effect.View = game.Camera.ViewMatrix;
      _effect.Projection = game.Camera.ProjectionMatrix;

      foreach (EffectPass pass in _effect.CurrentTechnique.Passes)
      {
        pass.Apply();

        GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, _vertices, 0, 4, _indices, 0, 2);
      }
      base.Draw(gameTime);
    }

  }

From my main Game class's Initialize() method, I simply instantiate this object and pass in the GameObject, Size and the Texture name I want to use:

_floor = new QuadDrawer(this, 100.0f, "Textures/checker");
  this.Components.Add(_floor);

Once the object is added to my Components collection, the Draw method of my QuadDrawer gets called and all should be good. Here is an image of what I am seeing. Note this should be solid gray with light color lines running in a grid pattern. The white artifacts appear to move as you move the camera around the surface, but disappear when you get close.

Here is picture showing the white artifacts:

Fuzzy

This is a close up showing how it should look: Close Up

Here is another picture showing how bad it can get from a distance: Better view of the problem

And here is what it should look like from a distance: enter image description here

The picture is not the best, but you can see what I am talking about. When the camera moves, it gets really bad. I've seen this texture used in other places and it works fine. Any idea what it could be?

I'd be happy to provide more info if you need it.

Thanks,

-Scott

like image 340
Scott Avatar asked Jun 23 '11 03:06

Scott


2 Answers

First of all you need to enable mipmaps for your texture. If it's just a stand-alone asset, you can select it in Solution Explorer, press F4 to get to properties, expand the "Content Processor" node and set "Generate Mipmaps" to true.

This article by Shawn Hargreaves explains why. Look at the difference between these two pictures from that article:

http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-70-20-metablogapi/2388.image_5F00_401AFB8B.png

http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-70-20-metablogapi/6518.image_5F00_1B896E07.png

(Looks a lot like your example images, doesn't it?)

Then, if you're going to be viewing at such oblique angles, you need to enable anisotropic filtering (Layoric explained how in his answer). This image from wikipedia illustrates what anisotropic filtering is for - note how it horizontally "un-blurs" the background (which was caused by the mipmaps).


(source: wikimedia.org)

like image 88
Andrew Russell Avatar answered Oct 26 '22 22:10

Andrew Russell


It looks like an anistropic filtering problem. I notice you do have GraphicsDevice.SamplerStates[0] = SamplerState.AnisotropicWrap; being used. I would suggest having a look at Shawn Hargreaves article on the topic and specifically the following lines.

device.SamplerStates[0].MinFilter = TextureFilter.Anisotropic;
device.SamplerStates[0].MagFilter = TextureFilter.Linear;
device.SamplerStates[0].MipFilter = TextureFilter.Linear;
device.SamplerStates[0].MaxAnisotropy = <n>;

Also,

Make sure the code is being set at the right time as a Effect.Begin will reset these filter settings. Another code snippet to help.

// inside draw perform any other required draw code  

// Call begin on the basic effect and on the pass to initialize BasicEffect's render settings  
basicEffect.Begin();  
basicEffect.CurrentTechnique.Passes[0].Begin();  

// Set your desired filter settings  
GraphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Anisotropic;  
GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Anisotropic;  
GraphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;  
GraphicsDevice.SamplerStates[0].MaxAnisotropy = 16;  

// Commit the changes to basic effect so it knows you made modifications  
basicEffect.CommitChanges();  

// Now perform you rendering to see Anisotropic filtering in effect  

code snippet thanks to Jeromy Wash over at AppHub forums, another great place to get help from the masters of XNA :)

HTH

like image 27
Darren Reid Avatar answered Oct 26 '22 23:10

Darren Reid