UE4+Cubemap(jpg导入UE4生成Cubemap)

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版权协议,转载请附上原文出处链接和本声明。