在使用MonoGame中的“Clamp”模式时,看到“Wrap”纹理(包括图片)

4
我将在我的MonoGame项目中使用SpriteBatch来呈现一个瓦片地图。
以下是渲染代码:
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black);

        SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, CameraManager.pInstance.pFinalTransform);

        foreach( TmxLayer Layer in mCurrentTileMap.Layers)
        {
            for (int y = 0; y < mCurrentTileMap.Height; y++)
            {
                for (int x = 0; x < mCurrentTileMap.Width; x++)
                {
                    TmxLayerTile Tile = Layer.Tiles[mCurrentTileMap.Width * y + x];
                    int TileID = -1;
                    Texture2D Texture = FindTileMapTextureForTileID(Tile.Gid, out TileID);
                    if (TileID > 0)
                    {
                        int atlasX = TileID % (Texture.Width / TileSize);
                        int atlasY = TileID / (Texture.Width / TileSize);

                        SpriteEffects FX = SpriteEffects.None;
                        if (Tile.HorizontalFlip)
                        {
                            FX = SpriteEffects.FlipHorizontally;
                        }

                        SpriteBatch.Draw(Texture, new Vector2(x * TileSize, y * TileSize), new Microsoft.Xna.Framework.Rectangle(atlasX * TileSize, atlasY * TileSize, TileSize, TileSize), Color.White, 0, Vector2.Zero, 1, FX, 0);
                    }
                }
            }
        }

以下是我认为重要的代码行:

SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, 
  null, null, null, CameraManager.pInstance.pFinalTransform);

SpriteBatch.Draw(Texture, new Vector2(x * TileSize, y * TileSize), 
  new Microsoft.Xna.Framework.Rectangle(atlasX * TileSize, atlasY * TileSize, TileSize, TileSize), 
  Color.White, 0, Vector2.Zero, 1, FX, 0);

如您所见,我正在使用SamplerState.PointClamp。然而,当我在世界中移动时,偶尔会看到下面一些瓷砖的单像素线,这些线是精灵图集中它下面的像素。

Enlarge to see issue better 点击放大以清晰地查看问题...

我非常确定这是因为应用于精灵批处理(CameraManager.pInstance.pFinalTransform)的变换使用了浮点精度。如果我强制将该变换的比例部分夹紧到整数,就不会出现问题。但是,这样做会导致相机运动非常抖动(我的游戏被放大了很多,因此每个像素在显示器上约占10个像素),所以这不是一个好的解决方案。

有什么想法可以避免这种效果吗?

以下是源纹理精灵表的参考图像。

enter image description here

1个回答

2
我相当确定这是因为应用于精灵批处理的变换(CameraManager.pInstance.pFinalTransform)使用了浮点精度。
是的,听起来很正确。在我们编写MonoGame.Extended中的Tiled地图渲染器时,我们遇到了类似的问题,当地图瓦片被相机缩放后。
解决方案是首先将未缩放的地图呈现到RenderTarget2D中,然后在第二次通过中缩放整个地图。这样,你不是每个单独的瓦片都进行缩放,而是整个地图进行缩放。
你可以看看MonoGame.Extended中的TiledMap class,了解它是如何工作的。
在您的情况下,代码可能如下所示:
private readonly RenderTarget2D _renderTarget;

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.SetRenderTarget(_renderTarget); 

    GraphicsDevice.Clear(Color.Black);

    SpriteBatch.Begin(sortMode: SpriteSortMode.Deferred, samplerState: SamplerState.PointClamp);

    foreach( TmxLayer Layer in mCurrentTileMap.Layers)
    {
        for (int y = 0; y < mCurrentTileMap.Height; y++)
        {
            for (int x = 0; x < mCurrentTileMap.Width; x++)
            {
                TmxLayerTile Tile = Layer.Tiles[mCurrentTileMap.Width * y + x];
                int TileID = -1;
                Texture2D Texture = FindTileMapTextureForTileID(Tile.Gid, out TileID);
                if (TileID > 0)
                {
                    int atlasX = TileID % (Texture.Width / TileSize);
                    int atlasY = TileID / (Texture.Width / TileSize);

                    SpriteEffects FX = SpriteEffects.None;
                    if (Tile.HorizontalFlip)
                    {
                        FX = SpriteEffects.FlipHorizontally;
                    }

                    SpriteBatch.Draw(Texture, new Vector2(x * TileSize, y * TileSize), new Microsoft.Xna.Framework.Rectangle(atlasX * TileSize, atlasY * TileSize, TileSize, TileSize), Color.White, 0, Vector2.Zero, 1, FX, 0);
                }
            }
        }
    }

    SpriteBatch.End();

    GraphicsDevice.SetRenderTarget(null);

    SpriteBatch.Begin(sortMode: SpriteSortMode.Immediate, blendState: BlendState.NonPremultiplied,
        samplerState: SamplerState.PointClamp, transformMatrix: CameraManager.pInstance.pFinalTransform);
    SpriteBatch.Draw(_renderTarget, Vector2.Zero, Color.White);
    SpriteBatch.End();
}

请注意,我已将相机变换移动到第二个SpriteBatch.Begin调用,并引入了一个新的_renderTarget成员变量。不要忘记在调用Draw之前声明_renderTarget。类似这样:
_renderTarget = new RenderTarget2D(graphicsDevice, width*tileWidth, height*tileHeight);

它需要足够大以呈现您的地图。理想情况下,它应该被优化为仅呈现可见部分的地图,但这是另一回事。

感谢您详细的回复。我已经尝试了您的建议,它确实解决了问题,但在此过程中我失去了“亚像素移动”。以前,一个游戏像素最终会渲染在4个或更多的屏幕像素上,因此字符(和相机)可以移动(并在)小于一个像素宽度的距离处进行渲染。现在,因为所有东西都在“真实”像素空间中呈现,如果一个角色向右移动(例如)0.25个像素,他们实际上不会在屏幕上移动。最终结果是非常抖动的运动。 - Goose
有趣的...显然我不知道细节,所以我不确定如何帮助你。但我仍然认为这是解决这个问题的正确方法。也许你可以发布一个关于子像素移动的新问题。 - craftworkgames
嗯,很难解释清楚。根据我在MonoGame.Extended中看到的情况,很可能存在这个问题。试着将最终相机的缩放倍数调高,比如说10倍(这样所有东西都会变得超级放大和方块状)。当一个精灵在世界空间移动一个像素时,在屏幕空间中它现在会移动10个像素(原来的10倍),看起来有些不正常。总之,这并不是需要“解决”的问题,而只是渲染目标方法的一种限制(再次感谢你详细的回答!!)。 - Goose
我不认为这会发生在MonoGame.Extended中,但我可能错了。如果我理解正确,你是将所有东西都渲染到渲染目标上了吗?你尝试过只渲染地图瓦片并将精灵作为第二个渲染通道进行渲染吗?另外,尝试关闭点夹紧功能,看看是否有帮助? - craftworkgames

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