168 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			168 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								title: UTexture2D的读取与写入数据并且保存成Asset
							 | 
						|||
| 
								 | 
							
								date: 2022-11-04 10:11:39
							 | 
						|||
| 
								 | 
							
								excerpt: 
							 | 
						|||
| 
								 | 
							
								tags: 
							 | 
						|||
| 
								 | 
							
								rating: ⭐
							 | 
						|||
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								## UTexture2D的读取与写入数据
							 | 
						|||
| 
								 | 
							
								在阅读@YivanLee的文章
							 | 
						|||
| 
								 | 
							
								https://zhuanlan.zhihu.com/p/45171840
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								我发现“**向UTexture2D写入数据**”这节有些纰漏:
							 | 
						|||
| 
								 | 
							
								1. 作者没有说明Texture2D->PlatformData->Mips[0].BulkData.LockReadOnly()返回空指针的详细情况。
							 | 
						|||
| 
								 | 
							
								2. 作者实现的写入是临时性的。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								在查阅国外资料时,我在isaratech的网站里找到解决方案,在此我将进行简单说明。(原文地址在结尾会介绍)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 从UTexture2D读取数据
							 | 
						|||
| 
								 | 
							
								读取方法如下:
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								const FColor* FormatedImageData = static_cast<const FColor*>( MyTexture2D->PlatformData->Mips[0].BulkData.LockReadOnly());
							 | 
						|||
| 
								 | 
							
								for(int32 X = 0; X < MyTexture2D->SizeX; X++)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    for (int32 Y = 0; Y < MyTexture2D->SizeY; Y++)
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        FColor PixelColor = FormatedImageData[Y * MyTexture2D->SizeX + X];
							 | 
						|||
| 
								 | 
							
								        // Do the job with the pixel
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								MyTexture2D->PlatformData->Mips[0].BulkData.Unlock();
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								但有的时候LockReadOnly()会返回空指针,这个时候你需要去检查UTexture2D的设置:
							 | 
						|||
| 
								 | 
							
								1. CompressionSettings 是否为VectorDisplacementmap。
							 | 
						|||
| 
								 | 
							
								2. MipGenSettings 是否为NoMipmaps。
							 | 
						|||
| 
								 | 
							
								3. SRGB 是否为未勾选状态。
							 | 
						|||
| 
								 | 
							
								  
							 | 
						|||
| 
								 | 
							
								但通常情况下都不可能同时符合这3个条件,所以我们需要将UTexture2D的3个选项设置成上述这3个状态。在处理所需操作后再进行恢复。代码如下:
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								TextureCompressionSettings OldCompressionSettings = MyTexture2D->CompressionSettings; TextureMipGenSettings OldMipGenSettings = MyTexture2D->MipGenSettings; bool OldSRGB = MyTexture2D->SRGB;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								MyTexture2D->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
							 | 
						|||
| 
								 | 
							
								MyTexture2D->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
							 | 
						|||
| 
								 | 
							
								MyTexture2D->SRGB = false;
							 | 
						|||
| 
								 | 
							
								MyTexture2D->UpdateResource();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const FColor* FormatedImageData = static_cast<const FColor*>( MyTexture2D->PlatformData->Mips[0].BulkData.LockReadOnly());
							 | 
						|||
| 
								 | 
							
								for(int32 X = 0; X < MyTexture2D->SizeX; X++)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    for (int32 Y = 0; Y < MyTexture2D->SizeY; Y++)
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        FColor PixelColor = FormatedImageData[Y * MyTexture2D->SizeX + X];
							 | 
						|||
| 
								 | 
							
								        //做若干操作
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								MyTexture2D->PlatformData->Mips[0].BulkData.Unlock();
							 | 
						|||
| 
								 | 
							
								Texture->CompressionSettings = OldCompressionSettings;
							 | 
						|||
| 
								 | 
							
								Texture->MipGenSettings = OldMipGenSettings;
							 | 
						|||
| 
								 | 
							
								Texture->SRGB = OldSRGB;
							 | 
						|||
| 
								 | 
							
								Texture->UpdateResource();
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 向UTexture2D写入数据
							 | 
						|||
| 
								 | 
							
								写入操作也是类似,不过我们需要调用若干UPackage与FAssetRegistryModule的代码。
							 | 
						|||
| 
								 | 
							
								下文中的Asset相关操作需要在包含头文件:
							 | 
						|||
| 
								 | 
							
								># include "AssetRegistryModule.h"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								同时需要在项目模块c#文件的PublicDependencyModuleNames.AddRange中加入"AssetTools"。
							 | 
						|||
| 
								 | 
							
								### 取得Package的指针
							 | 
						|||
| 
								 | 
							
								这里可以使用CreatePackage来创建新的包或是调用FindPackage来取得已有包的UPackage指针。包这个概念来源于UE3与UDK时代,当时引擎所有的资源都存放在一个一个的包中,修改过资源后还需要选中并且点击保存包选项,才能真正保存,但是UE4淡化了这个概念。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								你可以把包理解为Content下各个文件夹。
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								//创建名为PackageName值的包
							 | 
						|||
| 
								 | 
							
								//PackageName为FString类型
							 | 
						|||
| 
								 | 
							
								FString AssetPath = TEXT("/Game/")+ PackageName+ TEXT("/");
							 | 
						|||
| 
								 | 
							
								AssetPath += TextureName;
							 | 
						|||
| 
								 | 
							
								UPackage* Package = CreatePackage(NULL, *AssetPath);
							 | 
						|||
| 
								 | 
							
								Package->FullyLoad();
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								FindPackage本人还没用过,不过用法应该差不多。
							 | 
						|||
| 
								 | 
							
								### 取得UTexture2D的指针
							 | 
						|||
| 
								 | 
							
								你可以创建一个新的UTexture2D,也可以通过蓝图指定。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								//创建
							 | 
						|||
| 
								 | 
							
								UTexture2D* NewTexture = NewObject<UTexture2D>(Package, *TextureName, RF_Public | RF_Standalone | RF_MarkAsRootSet);
							 | 
						|||
| 
								 | 
							
								NewTexture->AddToRoot();            
							 | 
						|||
| 
								 | 
							
								NewTexture->PlatformData = new FTexturePlatformData();  
							 | 
						|||
| 
								 | 
							
								NewTexture->PlatformData->SizeX = TextureWidth;
							 | 
						|||
| 
								 | 
							
								NewTexture->PlatformData->SizeY = TextureHeight;
							 | 
						|||
| 
								 | 
							
								NewTexture->PlatformData->NumSlices = 1;
							 | 
						|||
| 
								 | 
							
								//设置像素格式
							 | 
						|||
| 
								 | 
							
								NewTexture->PlatformData->PixelFormat = EPixelFormat::PF_B8G8R8A8;
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								### 写入数据
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								//创建一个uint8的数组并取得指针
							 | 
						|||
| 
								 | 
							
								//这里需要考虑之前设置的像素格式
							 | 
						|||
| 
								 | 
							
								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);
							 | 
						|||
| 
								 | 
							
								        //这里可以设置4个通道的值
							 | 
						|||
| 
								 | 
							
								        //这里需要考虑像素格式,之前设置了PF_B8G8R8A8,那么这里通道的顺序就是BGRA
							 | 
						|||
| 
								 | 
							
								        Pixels[4 * curPixelIndex] = 100;
							 | 
						|||
| 
								 | 
							
								        Pixels[4 * curPixelIndex + 1] = 50;
							 | 
						|||
| 
								 | 
							
								        Pixels[4 * curPixelIndex + 2] = 20;
							 | 
						|||
| 
								 | 
							
								        Pixels[4 * curPixelIndex + 3] = 255;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								//创建第一个MipMap
							 | 
						|||
| 
								 | 
							
								FTexture2DMipMap* Mip = new FTexture2DMipMap();
							 | 
						|||
| 
								 | 
							
								NewTexture->PlatformData->Mips.Add(Mip);
							 | 
						|||
| 
								 | 
							
								Mip->SizeX = TextureWidth;
							 | 
						|||
| 
								 | 
							
								Mip->SizeY = TextureHeight;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								//锁定Texture让它可以被修改
							 | 
						|||
| 
								 | 
							
								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();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								//通过以上步骤,我们完成数据的临时写入
							 | 
						|||
| 
								 | 
							
								//执行完以下这两个步骤,编辑器中的asset会显示可以保存的状态(如果是指定Asset来获取UTexture2D的指针的情况下)
							 | 
						|||
| 
								 | 
							
								NewTexture->Source.Init(TextureWidth, TextureHeight, 1, 1, ETextureSourceFormat::TSF_BGRA8, Pixels);
							 | 
						|||
| 
								 | 
							
								NewTexture->UpdateResource();
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### 创建Asset并清理无用数据
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								Package->MarkPackageDirty();
							 | 
						|||
| 
								 | 
							
								FAssetRegistryModule::AssetCreated(NewTexture);
							 | 
						|||
| 
								 | 
							
								//通过asset路径获取包中文件名
							 | 
						|||
| 
								 | 
							
								FString PackageFileName = FPackageName::LongPackageNameToFilename(AssetPath, FPackageName::GetAssetPackageExtension());
							 | 
						|||
| 
								 | 
							
								//进行保存
							 | 
						|||
| 
								 | 
							
								bool bSaved = UPackage::SavePackage(Package, NewTexture, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *PackageFileName, GError, nullptr, true, true, SAVE_NoError);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								delete[] Pixels;
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								之后你就可以在编辑器中找到新生成的Asset。如果你是向选中的Asset写入数据(执行到上一步),调用UPackage::SavePackage即可进行保存。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 总结
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								本文包含以下知识:
							 | 
						|||
| 
								 | 
							
								1. UTexture2D的部分结构、属性以及读取设置方法。
							 | 
						|||
| 
								 | 
							
								2. UPackage部分的部分操作。
							 | 
						|||
| 
								 | 
							
								3. Asset的创建操作。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								读者可以执行通过阅读UPackage、FAssetRegistryModule的代码学习Asset更多的Asset操作。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## 个人推荐的isaratech网站文章
							 | 
						|||
| 
								 | 
							
								网站
							 | 
						|||
| 
								 | 
							
								https://isaratech.com/
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								生成一个新的贴图Asset保存
							 | 
						|||
| 
								 | 
							
								https://isaratech.com/save-a-procedurally-generated-texture-as-a-new-asset/
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								生成一个新的带有节点的MaterialAsset 
							 | 
						|||
| 
								 | 
							
								https://isaratech.com/ue4-programmatically-create-a-new-material-and-inner-nodes/
							 |