【问题标题】:Texture2D SetData errorTexture2D SetData 错误
【发布时间】:2014-12-19 05:58:05
【问题描述】:

我目前正在尝试在从文件加载后使用 SetData 在 Texture2D 对象上手动设置纹理数据。 Texture2D 是在启用 MipMaps 的情况下创建的。数据已从使用 DXT1 压缩保存的 .dds 文件加载,大小为 512x512。

这是负责创建纹理并将数据加载到其中的代码。

texture = new Texture2D(graphics, (int)ddsHeader.Width, (int)ddsHeader.Height, mipMapCount > 1, sFormat);

for (int i = 0; i < (int)mipMapCount; i++)
{
    int pow = (int)Math.Pow(2, i);

    int width = (int)ddsHeader.Width / pow;
    int height = (int)ddsHeader.Height / pow;

    Rectangle? rect = null;

    // get image size
    int blockSize = GetBlockSize(width, height);
    Console.WriteLine(string.Format("Width {0} Height {1}", width, height));
    Console.WriteLine("Block size: " + blockSize + " " + (int)ddsHeader.PitchOrLinearSize);
    // read the texture
    byte[] textureData = reader.ReadBytes(blockSize);
    rect = new Rectangle(0, 0, width, height);

    // set the color into the appropriate level of the texture
    if (sFormat == SurfaceFormat.Color && dxFormat == DXGIFormat.A8B8G8R8)
    {
        Color[] colors = ProcessUncompressedTexture(textureData, width, height);
        texture.SetData<Color>(i, rect, colors, 0, width * height);
    }
    else
    {
        texture.SetData<byte>(i, rect, textureData, 0, blockSize);
    }
}

它工作正常,数据被正确加载并正确设置到每个 mip 级别 - 直到它到达第 9 级,它会抛出以下内容:ArgumentException was unhandled: The size of the data passed in is too large or too small for this resource.

这是输出。

Width 512 Height 512        // level 0
Block size: 262144 262144
Width 256 Height 256        // level 1
Block size: 65536 262144
Width 128 Height 128        // level 2
Block size: 16384 262144
Width 64 Height 64        // level 3
Block size: 4096 262144
Width 32 Height 32        // level 4
Block size: 1024 262144
Width 16 Height 16        // level 5
Block size: 256 262144
Width 8 Height 8        // level 6
Block size: 64 262144
Width 4 Height 4        // level 7
Block size: 16 262144
Width 2 Height 2        // level 8
Block size: 4 262144

现在,textureData 的计数为 4。此 mip 级别的宽度和高度为 2。矩形的大小也是 2 个单位。我看不出是什么导致了这个错误的发生,因为之前的 8 个级别设置得很好。

有谁知道这里可能发生了什么导致这种情况?

【问题讨论】:

    标签: c# xna


    【解决方案1】:

    好的,我想我已经找到了答案,而且有点道理。

    这个线程here 似乎表明DXT 压缩mipmap 的最小允许大小是4x4 块。这与我所看到的相符,因为上面的 DXT1 和 5 错误,但如果我改用 DXGIFormat.A8B8G8R8,它会下降到 1x1 mip-mapped 大小的块。

    最小 4x4 块大小的原因是 DXT 压缩算法将块压缩到最小 4x1。

    无论如何,如果我使用的是 DXT 压缩纹理,我已经修改了我的代码以将要读取的块的大小调整为至少 4x4,并且它可以工作。

    为了下一个遇到同样问题的人(无论他们是谁),这里是我更新的 mipmap 集代码:

    // create the texture
    texture = new Texture2D(graphics, (int)ddsHeader.Width, (int)ddsHeader.Height, mipMapCount > 1, sFormat);
    
    Console.WriteLine(texture.LevelCount);
    
    for (int i = 0; i < (int)mipMapCount; i++)
    {
        int pow = (int)Math.Pow(2, i);
    
        int width = (int)ddsHeader.Width / pow;
        int height = (int)ddsHeader.Height / pow;
    
        Rectangle? rect = new Rectangle(0, 0, width, height);
    
        if (dxFormat == DXGIFormat.DXT1 || dxFormat == DXGIFormat.DXT5)
        {
            if (width < 4 && height < 4)
            {
                width = 4;
                height = 4;
            }
        }
    
        // get image size
        int blockSize = GetBlockSize(width, height);
        Console.WriteLine(string.Format("Width {0} Height {1}        // level {2}", width, height, i));
        Console.WriteLine("Block size: " + blockSize + " " + (int)ddsHeader.PitchOrLinearSize);
        // read the texture
        byte[] textureData = reader.ReadBytes(blockSize);
        Console.WriteLine("Data size: " + textureData.Length);
    
        // set the color into the appropriate level of the texture
        if (sFormat == SurfaceFormat.Color && dxFormat == DXGIFormat.A8B8G8R8)
        {
            Color[] colors = ProcessUncompressedTexture(textureData, width, height);
            texture.SetData<Color>(i, rect, colors, 0, width * height);
        }
        else
        {
            texture.SetData<byte>(i, rect, textureData, 0, blockSize);
        }
    }
    

    我向上移动了Rectangle? rect = ... 调用,并将 DXT 小于 4x4 块检测放置在其下方。它不是最整洁的,但它可以工作。

    【讨论】:

      【解决方案2】:

      您可能想看看图像压缩后的格式类型(问题的完整解释可以找到答案here):

      Here 是可能的格式类型。

      您将不得不检查texture.Format 并为其SurfaceFormat 使用正确的数据结构。

      例如。

      var b = new Bgr565[result.Width * result.Height];
      tex.SetData(b);
      

      下面的SurfaceFormat有对应的值类型可以使用。

      Color
      Bgr565
      Bgra5551
      Bgra4444
      NormalizedByte2
      NormalizedByte4
      Rgba1010102
      Rg32
      Rgba64
      Alpha8
      Single
      Vector2
      Vector4
      HalfSingle
      HalfVector2
      HalfVector4
      

      Dxt 格式表示纹理已压缩,您需要知道压缩后的大小、获取数据然后解压缩。

      您也许可以在某处找到 DXT1 和 DXT5 解压缩库。不幸的是,我找不到任何托管的东西,所以不安全的 C# 代码可能是转换它的最佳选择。根据维基百科,16 个像素存储在 8 个字节中,这使得每个像素有一个字节,因此理论上 byte[] data = new byte[(Width * Height) / 2] 应该可以用于提取数据。

      Dxt1
      Dxt3
      Dxt5
      

      这是一种特殊情况,只需使用HalfVector4 作为类型就可以了。 HdrBlendable

      【讨论】:

      • 问题是,一旦我指定了正确的表面格式,我只需要传递原始数据 - Texture2D 会自动解压缩/转换/应用它,因此纹理可以在我的游戏中使用。我在 dds 标头 DXT1 压缩中检测到,使用 SurfaceFormat.Dxt1 创建 Texture2D,并将压缩数据块传递给它,它就可以工作了。我需要手动转换的唯一时间是使用 DXGIFormat.A8B8G8R8 类型,将其更改为 DXGIFormat.A8R8G8B8 以便它与 SurfaceFormat.Color 正常工作
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-26
      • 1970-01-01
      • 2021-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多