在网上看到不少有关于UE4 生成Texture的教程,这些教程有些可以成功生成,有些生成失败,有些是创建无法持久保存的Texture或者说是关掉编辑器重启后就变黑的Texture等等,我跟着这些教程算是踩了不少小坑,有点难受,后面我还是决定看源码,最终算是从原理上清楚了Texture生成这个过程的一些原理。
FString TextureName = "Test";
FString PackName = FString::Printf(TEXT("/Game/%s"), *TextureName);
UPackage* Package = CreatePackage(*PackName);
Package->FullyLoad();
UTexture2D* Texture = NewObject(Package, *TextureName, RF_Public | RF_Standalone | RF_MarkAsRootSet);
static const int32 TextureWidth = 1024;
static const int32 TextureHeight = 1024;
TArray SourceColors;
SourceColors.Init(FColor::Red, TextureWidth * TextureHeight);
//Source
Texture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
Texture->Source.Init(TextureWidth, TextureHeight, 1, 1, ETextureSourceFormat::TSF_BGRA8);
uint8* SourceData = Texture->Source.LockMip(0);
FMemory::Memcpy(SourceData, SourceColors.GetData(), sizeof(FColor) * SourceColors.Num());
Texture->Source.UnlockMip(0);
//PlatformData
Texture->PlatformData = new FTexturePlatformData();
Texture->PlatformData->SizeX = TextureWidth;
Texture->PlatformData->SizeY = TextureHeight;
Texture->PlatformData->PixelFormat = EPixelFormat::PF_B8G8R8A8;
FTexture2DMipMap* NewMipMap = new FTexture2DMipMap();
Texture->PlatformData->Mips.Add(NewMipMap);
NewMipMap->SizeX = TextureWidth;
NewMipMap->SizeY = TextureHeight;
Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
uint8* MipData = (uint8*)Texture->PlatformData->Mips[0].BulkData.Realloc(sizeof(FColor) * TextureWidth * TextureHeight);
FMemory::Memcpy(MipData, SourceColors.GetData(), sizeof(FColor) * SourceColors.Num());
Texture->PlatformData->Mips[0].BulkData.Unlock();
Texture->UpdateResource();
Texture->PostEditChange();
Texture->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(Texture);
大体的主要三个步骤
(1)填充TextureSource资源
(2)填充PlatformData的Mip资源
(3)UpdateResource
首先反思下,上面的代码有何问题?可以运行下来创建了一张红色无Mip的纹理,大家可能是正常运行的代码,无任何问题。先不说其中的问题,先谈谈Texture几个概念
FTextureSouce存储了纹理的源数据(Color等等),这所谓纹理源数据,可以理解为一份外部导入,不可破坏的数据。或者说是编辑器数据.
可以看出纹理源数据只存在编辑时,最终Shipping打包出的项目是没这个东西。但是纹理源数据是为了解决什么问题的?从注释上看,是为了创建运行时的纹理数据(给材质Shader等情况使用的纹理数据). 假设不存在TextureSouce, 并且有个情况,我们需要调节纹理的对比度和亮度度等属性,第一次我们调亮度为了0.9,第二次我调亮度回到0.8. 第一次调节,假设原来为FColor(100, 100, 100), 则第一次调节后我们变为了FColor(90, 90, 90), 第二次变为多少?不知道,因为我们原始值已经变了,但是第二次调节亮度为0.8是相对于原始的,没了原始数据我们调节得到的颜色值根本无法知道,因此TextureSouce的作用是作为不可破坏的原始数据存在,Mip的生成都是遵从原始数据来的。TextureSouce不存在Mip或者说存在相当于第0级的Mip. 如下图所示:
FTexturePlatformData确切来说就是运行时数据,后面UE4 RenderThread的FTexture(对应于DX的SRV)就是靠FTexturePlatformData的各级Mip颜色数据(TArrat
FTextureResource PrivateResource就是我们在RenderThread使用的纹理资源来,GameThread的UTexture2d在RenderThread的代表。 如果我们发现材质,UI等需要用到SRV的情况下纹理上不正常甚至纹理相关的崩溃, PrivateResource是最优先Debug的。
上面说到Texture的FTextureSouce,FTexturePlatformData,FTextureResource三个概念。Texture2D给来一个接口把三个概念串联出来来。
也就是纹理生成,我们只要建立FTextureSource数据,然后UpdateResource就可以得到FTexturePlatformData和FTextureResource,也就是说来为了在编辑器生成纹理资源说不需要手动填充FTexturePlatformData,而且哪怕手动填充FTexturePlatformData也会因为调用UpdateResource(手动在编辑器调节亮度,对比度, Mip生成设定等都会触发UpdateResource, 导致旧的FPlatfromData被新生成的FPlatfromData数据覆盖掉)。
有个情况说:使用的Texture2d打包的游戏运行时创建的(不打算保存为编辑器文件),并非DevelopmentEditor或者DebugEditor这些模式下,也就是Texture2D不存在FTextureSource怎么办? 还是得看到上面的UpdateResource, 渲染资源FTextureResource是由FPlatfromData生成的,此时我们直接手动填充FTexturePlatformData的各级Mip数据,然后UpdateResource就行来。顺便说下UTexture2D已经提供来一个创建给完全运行时使用纹理的接口(只有一个Mip等级),但是手动填充FTexturePlatformData数据后,记住还得手动UpdateResource。
Texture2D.h, Texture2D.cpp, Texture.h, Texture.cpp, TextureDerivedData.cpp