我目前正尝试使用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. Rectangle的大小也是2个单位。我无法看到导致此错误发生的原因,因为前8个级别设置正常。
有谁知道这可能会发生什么导致这种情况?
答案 0 :(得分: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块检测放在它下面。它不是最整洁的,但它确实有效。
答案 1 :(得分:0)
您可能希望在图像压缩后查看格式类型(可以找到问题的完整说明,找到答案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