在XNA 4.0中渲染平面纹理时出现奇怪的“模糊”问题

5

我刚开始接触XNA,很确定自己错过了一些非常简单的东西。我绘制了一个四边形平面作为地面,并在上面贴了一个2D纹理。这个纹理靠近时看起来很好,但是当我移动相机时,我看到一堆白色伪像遍布整个画面。它们随着我靠近它们而消失。我猜测这是采样问题或类似的问题,但我卡住了。

首先,我创建了一个QuadDrawer类来为我绘制平面。它派生自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);
    }

  }

在我的主游戏类的Initialize()方法中,我只需实例化这个对象并传入GameObject、Size和我想要使用的Texture名称:

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

一旦对象被添加到我的组件集合中,我的QuadDrawer的绘制方法就会被调用,一切都应该很好。这是我所看到的图像。请注意,这应该是纯灰色的,带有网格样式的浅色线条。白色伪影似乎随着你移动摄像机在表面上移动而移动,但当你靠近时就消失了。
下面是展示白色伪影的图片: Fuzzy 这是一个放大图,显示正常状态: Close Up 下面是另一张图片,从远处看问题更明显: Better view of the problem 这是从远处看起来的样子: enter image description here 照片不太清楚,但你可以看出我的意思。当摄像机移动时,情况变得非常糟糕。我已经看到这个纹理在其他地方使用,并且很好地工作。你有什么想法吗?
如果需要更多信息,请告诉我。
谢谢,
-Scott

尽管这些工件可能对您来说很明显,但我仍然很难看到您所描述的问题。您能否提供一个更好的示例图像,并可能圈出所述工件的实例?谢谢。 - Darren Reid
我添加了另一张图片,它展示得更好。所有的白线都不应该存在。在新的图片中,你可以看到当白色网格线离相机越远时开始旋转。我会上传另一张图片,展示我期望的效果。顺便说一下,这种情况发生在我应用任何纹理时,而不仅仅是这一个。谢谢! - Scott
@Layoric - 我在底部添加了另一张图片,展示了场景从远处看应该是什么样子。非常感谢任何帮助... - Scott
3个回答

6
首先,您需要为纹理启用mipmaps。如果它只是一个独立的资源,您可以在“解决方案资源管理器”中选择它,按F4键进入属性,展开“内容处理器”节点并将“生成mipmaps”设置为true。 Shawn Hargreaves的这篇文章解释了原因。请看一下该文章中这两张图片之间的差异:

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

(看起来很像你的示例图像,不是吗?)

然后,如果您将以这样的斜角查看,则需要启用各向异性过滤(Layoric在他的答案中解释了如何做到这一点)。wikipedia上的这张图片说明了各向异性过滤的作用-请注意它如何在水平方向上“去模糊”背景(这是由mipmaps引起的)。


(来源:wikimedia.org


这完全解决了我的问题。显然我还有很多要学习的。谢谢! - Scott

2
看起来像是各向异性过滤问题。我注意到你正在使用GraphicsDevice.SamplerStates[0] = SamplerState.AnisotropicWrap;,建议你查看Shawn Hargreaves关于该主题的文章,特别是以下部分。
device.SamplerStates[0].MinFilter = TextureFilter.Anisotropic;
device.SamplerStates[0].MagFilter = TextureFilter.Linear;
device.SamplerStates[0].MipFilter = TextureFilter.Linear;
device.SamplerStates[0].MaxAnisotropy = <n>;

此外,请确保代码在正确的时间设置为Effect.Begin,否则这些过滤器设置将被重置。以下是另一个有用的代码片段。
// 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  

感谢来自Jeromy WashAppHub论坛的代码片段,这里是另一个向XNA大师寻求帮助的好地方 :)

希望对您有所帮助。


谢谢提供链接。我阅读了博客文章并尝试了不同类型的过滤器,但都没有解决我的问题。即使删除了AnisotropicWrap采样器,我仍然会得到伪影。我还不够了解这方面的知识,无法找到问题所在... - Scott
更新了一些建议,@Scott。 - Darren Reid
我相信4.0已经取消了BasicEffect.Begin()和BasicEffect.CommitChanges()。然而,我使用你上面建议的过滤器(使用4.0语法)并且仍然遇到同样的问题。我将从QuadDrawer类中提取所有代码并直接添加到我的Game类中,以查看是否可能是该类的问题。感谢您的帮助。如果您有更多建议...我很乐意听取。 - Scott
你是否在你的效果回路中分配了滤波器? - Darren Reid

1

各向异性过滤不会有所帮助。你最好的解决方案(虽然不便宜)是使用超采样,这将绝对解决你的问题,但价格相当昂贵,或者使用多重采样,这将根据你使用的正方形几何图形来解决你的问题。

在我的个人经验中,多重采样通常可以保持开启状态,因为现代显卡具有渲染它而不会给你带来太大成本的能力,除非你正在构建一个非常GPU密集型的应用程序/游戏。此外,即使超采样使用相当昂贵,也不完全不可行,原因与上述相同,但请测试以确保你不会受到巨大的性能惩罚。


虽然各向异性过滤 单独使用 不能解决问题(尽管如果正确使用,它会有所帮助),但超采样只会 减轻 问题而不是解决它。如果这是几何问题,多重采样会有所帮助 - OP正在使用纹理(并且再次只会减轻问题)。请参阅我回答中链接的文章,以深入解释为什么这两个解决方案无法解决问题。 - Andrew Russell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接