UE4动态纹理资产化

假设我们在编辑器中从 C++ 生成了一个 UTexture2D。 保存它以便以后使用可能会很有趣。 让我们看看如何做到这一点。

第一步是创建一个 UTexture2D 对象。 在这里,我们不能使用 UTexture2D::CreateTransient 创建瞬态纹理(Transient Texture),因为如果这样做,我们将无法保存纹理(瞬态对象无法保存)。

因此,我们将创建一个包,然后在这个包中存储一个新的纹理。

FString PackageName = TEXT("/Game/ProceduralTextures/");
PackageName += TextureName;
UPackage* Package = CreatePackage(NULL, *PackageName);
Package->FullyLoad();

UTexture2D* NewTexture = NewObject<UTexture2D>(Package, *TextureName, RF_Public | RF_Standalone | RF_MarkAsRootSet);

在这里,我们创建一个包 /Game/ProceduralTextures/,并在调用 NewObject 时在该包中添加一个名为 TextureName 的纹理。

1、填充纹理

一旦我们有了纹理,就可以初始化它并用我们的数据填充它。

NewTexture->AddToRoot();				// This line prevents garbage collection of the texture
NewTexture->PlatformData = new FTexturePlatformData();	// Then we initialize the PlatformData
NewTexture->PlatformData->SizeX = TextureWidth;
NewTexture->PlatformData->SizeY = TextureHeight;
NewTexture->PlatformData->NumSlices = 1;
NewTexture->PlatformData->PixelFormat = EPixelFormat::PF_B8G8R8A8;

到目前为止,它与我们在创建瞬态纹理时所做的非常相似:用纹理参数(宽度、高度、像素格式)填充 PlatformData 对象。 这里我们选择 PF_B8G8R8A8 作为像素格式,但我们可以选择任何像素格式,只要我们之后相应地填充数据即可。

然后,是时候填充纹理了:

uint8* Pixels = new uint8[TextureWidth * TextureHeight * 4];
for (int32 y = 0; y < TextureHeight; y++)
{
	for (int32 x = 0; x < TextureWidth; x++)
	{
		int32 curPixelIndex = ((y * TextureWidth) + x);
		Pixels[4 * curPixelIndex] = B;
		Pixels[4 * curPixelIndex + 1] = G;
		Pixels[4 * curPixelIndex + 2] = R;
		Pixels[4 * curPixelIndex + 3] = A;
	}
}

有不同的方法来填充纹理。 在这个小代码片段中,我们只是创建一个像素数组并为每个像素数组定义一种颜色。 数组大小是每像素的宽度 x 高度 x 字节数。 由于像素格式为 BGRA,因此每个像素有四个字节。 我们可能会注意到像素是按照 BGRA 顺序填充的,以匹配先前选择的像素格式。

然后,我们需要用像素数组中的数据填充纹理:

// Allocate first mipmap.
FTexture2DMipMap* Mip = new(NewTexture->PlatformData->Mips) FTexture2DMipMap();
Mip->SizeX = TextureWidth;
Mip->SizeY = TextureHeight;

// Lock the texture so it can be modified
Mip->BulkData.Lock(LOCK_READ_WRITE);
uint8* TextureData = (uint8*) Mip->BulkData.Realloc(TextureWidth * TextureHeight * 4);
FMemory::Memcpy(TextureData, Pixels, sizeof(uint8) * TextureHeight * TextureWidth * 4);
Mip->BulkData.Unlock();

至此我们已经生成了纹理。 现在,我们要将其保存为新资产。

2、保存纹理

到目前为止,我们只在 PlatformData 中设置了数据。 但是,PlatformData 是暂时的,无法保存在磁盘上。 要在纹理的非瞬态字段中初始化数据,我们将引用 Source字段:

NewTexture->Source.Init(
    TextureWidth, 
    TextureHeight, 
    1, 
    1, 
    ETextureSourceFormat::TSF_BGRA8, 
    Pixels
);

调用此函数将初始化 Source 对象,设置其宽度、高度、像素格式以及 Pixels 数组中的数据。

最后,我们将调用  SavePackage 来保存新创建的资产:

NewTexture->UpdateResource();
Package->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(NewTexture);

FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
bool bSaved = UPackage::SavePackage(Package, NewTexture, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *PackageFileName, GError, nullptr, true, true, SAVE_NoError);

delete[] Pixels;	// Don't forget to free the memory here

原文链接:UE4 – Save a procedurally generated texture as a new asset

BimAnt翻译整理,转载请标明出处