VS:2019
虚幻引擎:4.25
Python:官方2.7版本
目的:通过加载外部的jpg全景图,直接在UE4中生成Texture Cube格式
起因:我们将一张外部的jpg导入UE4后,会在资源管理器中生成Texture纹理贴图资源,然后需要将Texture纹理贴图导出成HDR,之后再将HDR导入UE4中,才会生成Texture Cube格式。如果原始jpg分辨率高,图片多的话,整个流程会特别慢。
思路:分成两部分,第一部分通过读取jpg图片数据,直接在内存中生成Texture2D。第二部分我们可以通过SceneCaptureCube这个组件,去渲染球体场景,来填充RenderTargetCube,进而生成Texture Cube格式。而球体场景可以通过第一部分的Texture2D,作为球体材质实例的图片。
一、场景:
Content/RenderTargetCube目录说明:
- M_Sphere是母材质
- MI_Sphere是材质实例
- T_Init是初始的材质贴图
- RTC_Sphere是场景中SceneCaptureCube需要用到的RenderTargetCube
场景说明:
- 场景里有个Sphere的静态模型,我们给它赋上自己创建的材质实例,注意Sphere模型的Scale值,z是-5和镜像有关。
- 场景在球心的位置有一个SceneCaptureCube这个Actor,设置它的TextureTarget,以及一些设置,记得勾选CaptureRotation让旋转生效。然后注意它的Rotation,也是和镜像相关。
二、代码:
- 加载jpg,创建Texture2D,并修改MI_Sphere材质实例的贴图
.h
UFUNCTION(BlueprintCallable)
static UTexture2D* LoadTexture(UMaterialInstanceConstant* _MaterialInstanceConstant,const FString _Path, bool& _IsValid, int32& _OutWidth, int32& _OutHeight);
.cpp
static TSharedPtr<IImageWrapper> GetImageWrapperByExtention(const FString InImagePath)
{
IImageWrapperModule& mImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
if (InImagePath.EndsWith(".png"))
{
return mImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
}
else if (InImagePath.EndsWith(".jpg") || InImagePath.EndsWith(".jpeg"))
{
return mImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
}
else if (InImagePath.EndsWith(".bmp"))
{
return mImageWrapperModule.CreateImageWrapper(EImageFormat::BMP);
}
else if (InImagePath.EndsWith(".ico"))
{
return mImageWrapperModule.CreateImageWrapper(EImageFormat::ICO);
}
else if (InImagePath.EndsWith("exr"))
{
return mImageWrapperModule.CreateImageWrapper(EImageFormat::EXR);
}
else if (InImagePath.EndsWith(".icns"))
{
return mImageWrapperModule.CreateImageWrapper(EImageFormat::ICNS);
}
return nullptr;
}
UTexture2D* UExportTextureFunctionLibrary::LoadTexture(UMaterialInstanceConstant* _MaterialInstanceConstant, const FString _Path, bool& _IsValid, int32& _OutWidth, int32& _OutHeight)
{
UTexture2D* mTexture2D = nullptr;
_IsValid = false;
// 判断路径下是否存在图片.
if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*_Path))
{
return nullptr;
}
// 从文件中加载压缩的字节数据.
TArray<uint8> mRawFileData;
if (!FFileHelper::LoadFileToArray(mRawFileData, *_Path))
{
return nullptr;
}
// 使用ImageWrapper模块检测图像类型.
TSharedPtr<IImageWrapper> ImageWrapper = GetImageWrapperByExtention(_Path);
// 解压图像数据.
if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(mRawFileData.GetData(), mRawFileData.Num()))
{
TArray<uint8> mUnCompressedRGBA;
if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, mUnCompressedRGBA))
{
// 创建Texture2D.
mTexture2D = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
if (mTexture2D != nullptr)
{
_IsValid = true;
_OutWidth = ImageWrapper->GetWidth();
_OutHeight = ImageWrapper->GetHeight();
void* TextureData = mTexture2D->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
FMemory::Memcpy(TextureData, mUnCompressedRGBA.GetData(), mUnCompressedRGBA.Num());
mTexture2D->PlatformData->Mips[0].BulkData.Unlock();
mTexture2D->UpdateResource();
}
}
}
// 修改MI_Sphere材质实例的贴图.
if(mTexture2D)
UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(_MaterialInstanceConstant, FName(TEXT("Texture")), mTexture2D);
return mTexture2D;
}
- 刷新SceneCaptureCube,将最新的RTC_Sphere创建TextureCube
.h
UFUNCTION(BlueprintCallable)
static void CreateCubemap(class UTextureRenderTargetCube* _TextureRenderTargetCube, FString _PackagePath, FString _AssetName);
.cpp
void UExportTextureFunctionLibrary::CreateCubemap(class UTextureRenderTargetCube* _TextureRenderTargetCube, FString _PackagePath, FString _AssetName)
{
UPackage* mPackage = CreatePackage(NULL, *_PackagePath);
UTextureCube* mTextureCube = _TextureRenderTargetCube->ConstructTextureCube(mPackage, _AssetName, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone);
if (mTextureCube)
{
mTextureCube->MarkPackageDirty();
mTextureCube->GetOuter()->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(mTextureCube);
UE_LOG(LogTemp, Log, TEXT("cube full path:%s"), *(mTextureCube->GetFullName()));
}
}
- 我们通过python脚本,将这两部分连接起来,能够让加载进来的jpg,生成Texture2D,并修改MI_Sphere材质实例。然后场景中的SceneCaptureCube刷新,将最新的RTC_Sphere场景生成jpg对应的TextureCube。
jpg_to_cubemap.py
import unreal
import os
# part1 这部分是从外部导入jpg,需要的Setting.
def build_auto_import_data(filenames, destination_path):
auto_import_data = unreal.AutomatedAssetImportData()
# set filenames that need be imported
auto_import_data.set_editor_property("filenames", filenames)
# set destination_path
auto_import_data.set_editor_property("destination_path", destination_path)
# set replace
auto_import_data.set_editor_property("replace_existing", True)
return auto_import_data
# part2 这部分是通过外部的路径,将jpg导入到UE4中.
def import_jpg():
file_dir=r'C:\Users\Administrator\Desktop\Camera\202000000000009\images'
file_list = os.listdir(file_dir)
jpg_list = [item for item in file_list if item.endswith('.jpg')]
jpg_os_list=[]
jpg_ue_list=[]
for jpg in jpg_list:
jpg_os = os.path.join(file_dir, jpg)
jpg_os_list.append(jpg_os)
asset_name = str(jpg).split('.')[1]
jpg_ue = '/Game/Texture/' + asset_name
jpg_ue_list.append(jpg_ue)
import_data=build_auto_import_data(jpg_os_list, '/Game/Texture')
unreal.AssetToolsHelpers.get_asset_tools().import_assets_automated(import_data)
unreal.EditorAssetLibrary.save_directory('/Game/Texture')
# part3 这部分是单张图片生成Cubemap.
def create_cubemap():
mi_asset_path = '/Game/RenderTargetCube/MI_Sphere.MI_Sphere'
mi_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(mi_asset_path)
mi_asset_object = mi_asset_data.get_asset()
tex_asset_path = '/Game/Texture/camera_1vB44rwa.camera_1vB44rwa'
tex_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(tex_asset_path)
tex_asset_object = tex_asset_data.get_asset()
tex_asset_name = tex_asset_data.get_editor_property('asset_name')
unreal.MaterialEditingLibrary.set_material_instance_texture_parameter_value(mi_asset_object, 'Texture', tex_asset_object)
rtc_asset_path = '/Game/RenderTargetCube/RTC_Sphere.RTC_Sphere'
rtc_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(rtc_asset_path)
rtc_asset_object = rtc_asset_data.get_asset()
scene_capture_cube_array = unreal.GameplayStatics.get_all_actors_of_class(unreal.EditorLevelLibrary.get_editor_world(), unreal.SceneCaptureCube)
scene_capture_cube = scene_capture_cube_array[0]
capture_component_cube = scene_capture_cube.get_editor_property('capture_component_cube')
capture_component_cube.update_content()
cubemap_package_path = '/Game/HDR/' + str(tex_asset_name)
unreal.ExportTextureFunctionLibrary.create_cubemap(rtc_asset_object, cubemap_package_path, str(tex_asset_name))
# part4 这部分是通过加载图片,然后生成Cubemap.
def create_cubemap_by_texture2d():
mi_asset_path = '/Game/RenderTargetCube/MI_Sphere.MI_Sphere'
mi_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(mi_asset_path)
mi_asset_object = mi_asset_data.get_asset()
rtc_asset_path = '/Game/RenderTargetCube/RTC_Sphere.RTC_Sphere'
rtc_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(rtc_asset_path)
rtc_asset_object = rtc_asset_data.get_asset()
scene_capture_cube_array = unreal.GameplayStatics.get_all_actors_of_class(unreal.EditorLevelLibrary.get_editor_world(), unreal.SceneCaptureCube)
scene_capture_cube = scene_capture_cube_array[0]
capture_component_cube = scene_capture_cube.get_editor_property('capture_component_cube')
file_dir = r'C:\Users\Administrator\Desktop\Camera\202000000000009\images'
file_list = os.listdir(file_dir)
jpg_list = [item for item in file_list if item.endswith('.jpg')]
for jpg_name in jpg_list:
jpg_path = os.path.join(file_dir, jpg_name)
tex_asset_name = str(jpg_name).split('.')[0]
unreal.ExportTextureFunctionLibrary.load_texture(mi_asset_object, jpg_path)
capture_component_cube.update_content()
cubemap_package_path = '/Game/HDR/' + str(tex_asset_name)
unreal.ExportTextureFunctionLibrary.create_cubemap(rtc_asset_object, cubemap_package_path, str(tex_asset_name))
# part5 这部分是part3的多张图的自动化操作.
def create_cubemap_auto():
mi_asset_path = '/Game/RenderTargetCube/MI_Sphere.MI_Sphere'
mi_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(mi_asset_path)
mi_asset_object = mi_asset_data.get_asset()
tex_asset_path = '/Game/Texture'
tex_asset_data_array = unreal.AssetRegistryHelpers.get_asset_registry().get_assets_by_path(tex_asset_path)
rtc_asset_path = '/Game/RenderTargetCube/RTC_Sphere.RTC_Sphere'
rtc_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(rtc_asset_path)
rtc_asset_object = rtc_asset_data.get_asset()
scene_capture_cube_array = unreal.GameplayStatics.get_all_actors_of_class(unreal.EditorLevelLibrary.get_editor_world(), unreal.SceneCaptureCube)
scene_capture_cube = scene_capture_cube_array[0]
capture_component_cube = scene_capture_cube.get_editor_property('capture_component_cube')
for tex_asset_data in tex_asset_data_array:
tex_asset_object = tex_asset_data.get_asset()
tex_asset_name = tex_asset_data.get_editor_property('asset_name')
unreal.MaterialEditingLibrary.set_material_instance_texture_parameter_value(mi_asset_object, 'Texture', tex_asset_object)
capture_component_cube.update_content()
cubemap_package_path = '/Game/HDR/' + str(tex_asset_name)
unreal.ExportTextureFunctionLibrary.create_cubemap(rtc_asset_object, cubemap_package_path, str(tex_asset_name))
if __name__ == "__main__":
# import_jpg()
# create_cubemap_auto()
# all 只用这个就行.
create_cubemap_by_texture2d()
这样,整个流程大幅提升。本人测试结果:16张全景图(6720x3360),导入UE4生成TextureCube,大约10s
版权声明:本文为zhangmei126原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。