最近在做GPU刷草插件的迭代,其中地表高度信息在预烘焙时由采样器采样得到,数据是经过归一化处理的二维浮点数组 float[,]
。我的做法是将这些原始数据先转化为Texture2D格式的纹理:
public static Texture2D GetTextureByArrayData(float[,] raw, int len, TextureFormat format)
{
var heightmapData = new Color[len * len];
int index = 0;
for (int i = 0; i < len; ++i)
{
for (int j = 0; j < len; ++j)
{
heightmapData[index++] = new Color(raw[i, j], 0, 0, 0);
}
}
var tex = new Texture2D(len, len, format, true, true);
tex.SetPixels(heightmapData, 0);
tex.Apply();
return tex;
}
此时将纹理渲染出来可以得到期望的结果:
然后为了获得最快的运行时读取和加载速度,我把纹理的原始字节流无编码的存到本地磁盘:
File.WriteAllBytes($"{path}{name}", tex.GetRawTextureData());
最后在运行时动态的读入字节流,从新封装成ReadOnly模式的Texture2D纹理:
public static Texture2D LoadTex2DFromRawData(IAssetLoader aLoader, string aPath, int aFileSize, int aTexSize, TextureFormat aTexFormat, FilterMode aFilterMode, bool aNeedMip = false, bool aReadOnly = true)
{
var rawData = aLoader.LoadFileContentNativeBytes(aPath, aFileSize);
if (rawData.Length <= 0)
{
Log.Info($"load rawData from file failed, path:{aPath}");
return null;
}
bool needLiner = true;
if (aFilterMode == FilterMode.Point)
needLiner = false;
var tex2D = new Texture2D(aTexSize, aTexSize, aTexFormat, aNeedMip, needLiner);
tex2D.filterMode = aFilterMode;
tex2D.LoadRawTextureData(rawData);
tex2D.Apply(aNeedMip, aReadOnly);
rawData.Dispose();
return tex2D;
}
然而将重新生成的纹理渲染出来显示,结果产生了异变:
仔细排查后并没有发现语法层面有任何问题,也没有发现什么设置能够导致图像发送如此变形。一些列未果的尝试后,我又回到了问题的本源:前后2张图片的差异倒地在哪儿?对比后发现,变形图片从上到下每一行对比原始图片,似乎少了1个像素,而且误差是累加的,从而导致变形图片只有最上面第一行和原始结果相一致,越向中间延伸,误差越大,图像错位越多,然后随着累积误差越接近一整行,这种视觉上的错位反而减少,最终在最后一行又回归“正确”。
依据这个发现很快发现了问题点:2张图的分辨率不同。
原始图像是覆盖256m见方地块的高度图,分辨率是257 X 257
,而新转换出来的图片使用了错误的256 X 256
的分辨率,由于是从原始大小为66049
字节中重建一张 65535
大小的图像,Unity的Texture2D在执行LoadRawTextureData(bytes)
时可见是按照行分辨率大小逐行填充,从而导致正确信息被提前被截断,产生如图的错位。