虚幻引擎:4.25
Python:官方2.7版本
通过Python脚本读取相机相关数据的Json文件,然后创建Sequence,拍摄图片
可以在此基础上修改,做一个自定义的拍摄工具,方便快捷
create_sequence.py
import unreal, os, json, math
# sequence asset path
sequence_asset_path = '/Game/Render_Sequence.Render_Sequence'
# read json file
def read_json_file(path):
file = open(path, "rb")
file_json = json.load(file)
export_path = file_json['output_image_path'] # image export path
film_fov = file_json.get('film_fov', None) # camera film fov
film_resolution = file_json.get("film_resolution", 1680) # camera file resolution
delay_every_frame = file_json.get("delay_every_frame", 3.0) # sequence run delay every frame
camera_json = file_json['camera_data'] # camera data
camera_transform_array = []
camera_rotation_array = []
camera_name_array = []
for camera in camera_json:
camera_location = camera_json[camera]["relative_location"]
camera_rotation = camera_json[camera]["relative_rotation"]
camera_transform_array.append(unreal.Transform(location=[camera_location[0], camera_location[1], camera_location[2]], rotation=[camera_rotation[1],0.0,camera_rotation[0]], scale=[1.0,1.0,1.0]))
camera_rotation_array.append(unreal.Rotator(roll=camera_rotation[0], pitch=camera_rotation[1], yaw=0.0))
camera_name_array.append(camera)
return camera_transform_array, camera_rotation_array, camera_name_array, export_path, film_fov, film_resolution, delay_every_frame
def create_sequence(asset_name, camera_transform_array, camera_rotation_array, camera_name_array, film_fov, package_path = '/Game/'):
# create a sequence
sequence = unreal.AssetToolsHelpers.get_asset_tools().create_asset(asset_name, package_path, unreal.LevelSequence, unreal.LevelSequenceFactoryNew())
sequence.set_display_rate(unreal.FrameRate(numerator=1, denominator=1))
sequence.set_playback_start_seconds(0)
sequence.set_playback_end_seconds(6 * len(camera_transform_array))
# add a camera cut track
camera_cut_track = sequence.add_master_track(unreal.MovieSceneCameraCutTrack)
for index in range(0, len(camera_transform_array)):
camera_cut_section = camera_cut_track.add_section()
camera_cut_section.set_start_frame_bounded(6 * index)
camera_cut_section.set_end_frame_bounded(6 * index + 6)
camera_cut_section.set_start_frame_seconds(6 * index)
camera_cut_section.set_end_frame_seconds(6 * index + 6)
# Create a cine camera actor
camera_actor = unreal.EditorLevelLibrary().spawn_actor_from_class(unreal.CineCameraActor, unreal.Vector(0,0,0), unreal.Rotator(0,0,0))
camera_actor.set_actor_label(camera_name_array[index])
print(camera_name_array[index])
camera_component = camera_actor.get_cine_camera_component()
ratio = math.tan(film_fov/360.0*math.pi)*2
filmback = camera_component.get_editor_property("filmback")
focal_length = camera_component.get_editor_property("current_focal_length")
filmback.set_editor_property("sensor_height", ratio*focal_length)
filmback.set_editor_property("sensor_width", ratio*focal_length)
focus_settings = camera_component.get_editor_property("focus_settings")
focus_settings.set_editor_property("focus_method", unreal.CameraFocusMethod.DISABLE)
# add a binding for the camera
camera_binding = sequence.add_possessable(camera_actor)
transform_track = camera_binding.add_track(unreal.MovieScene3DTransformTrack)
transform_section = transform_track.add_section()
transform_section.set_start_frame_bounded(6 * index)
transform_section.set_end_frame_bounded(6 * index + 6)
transform_section.set_start_frame_seconds(6 * index)
transform_section.set_end_frame_seconds(6 * index + 6)
# get channel for location_x location_y location_z
channel_location_x = transform_section.get_channels()[0]
channel_location_y = transform_section.get_channels()[1]
channel_location_z = transform_section.get_channels()[2]
# get key for rotation_y rotation_z
channel_rotation_x = transform_section.get_channels()[3]
channel_rotation_y = transform_section.get_channels()[4]
channel_rotation_z = transform_section.get_channels()[5]
camera_transform = camera_transform_array[index]
camera_location_x = camera_transform.get_editor_property("translation").get_editor_property("x")
camera_location_y = camera_transform.get_editor_property("translation").get_editor_property("y")
camera_location_z = camera_transform.get_editor_property("translation").get_editor_property("z")
new_time = unreal.FrameNumber(value = 6*index + 0)
channel_location_x.add_key(new_time, camera_location_x, 0.0)
channel_location_y.add_key(new_time, camera_location_y, 0.0)
channel_location_z.add_key(new_time, camera_location_z, 0.0)
channel_rotation_x.add_key(new_time, -90.0, 0.0)
channel_rotation_y.add_key(new_time, 00.0, 0.0)
channel_rotation_z.add_key(new_time, 0.0, 0.0)
new_time1 = unreal.FrameNumber(value =6 *index + 1)
channel_location_x.add_key(new_time1, camera_location_x, 0.0)
channel_location_y.add_key(new_time1, camera_location_y, 0.0)
channel_location_z.add_key(new_time1, camera_location_z, 0.0)
channel_rotation_x.add_key(new_time1, -90.0, 0.0)
channel_rotation_y.add_key(new_time1, 90.0, 0.0)
channel_rotation_z.add_key(new_time1, 0.0, 0.0)
new_time2 = unreal.FrameNumber(value = 6*index + 2)
channel_location_x.add_key(new_time2, camera_location_x, 0.0)
channel_location_y.add_key(new_time2, camera_location_y, 0.0)
channel_location_z.add_key(new_time2, camera_location_z, 0.0)
channel_rotation_x.add_key(new_time2, -90.0, 0.0)
channel_rotation_y.add_key(new_time2, 180.0, 0.0)
channel_rotation_z.add_key(new_time2, 0.0, 0.0)
new_time3 = unreal.FrameNumber(value = 6*index + 3)
channel_location_x.add_key(new_time3, camera_location_x, 0.0)
channel_location_y.add_key(new_time3, camera_location_y, 0.0)
channel_location_z.add_key(new_time3, camera_location_z, 0.0)
channel_rotation_x.add_key(new_time3, -90.0, 0.0)
channel_rotation_y.add_key(new_time3, 270.0, 0.0)
channel_rotation_z.add_key(new_time3, 0.0, 0.0)
new_time4 = unreal.FrameNumber(value = 6*index + 4)
channel_location_x.add_key(new_time4, camera_location_x, 0.0)
channel_location_y.add_key(new_time4, camera_location_y, 0.0)
channel_location_z.add_key(new_time4, camera_location_z, 0.0)
channel_rotation_x.add_key(new_time4, -90.0, 0.0)
channel_rotation_y.add_key(new_time4, 0.0, 0.0)
channel_rotation_z.add_key(new_time4, -90.0, 0.0)
new_time5 = unreal.FrameNumber(value = 6*index + 5)
channel_location_x.add_key(new_time5, camera_location_x, 0.0)
channel_location_y.add_key(new_time5, camera_location_y, 0.0)
channel_location_z.add_key(new_time5, camera_location_z, 0.0)
channel_rotation_x.add_key(new_time5, -90.0, 0.0)
channel_rotation_y.add_key(new_time5, 0.0, 0.0)
channel_rotation_z.add_key(new_time5, 90.0, 0.0)
# add the binding for the camera cut section
camera_binding_id = sequence.make_binding_id(camera_binding, unreal.MovieSceneObjectBindingSpace.LOCAL)
camera_cut_section.set_camera_binding_id(camera_binding_id)
# save sequence asset
unreal.EditorAssetLibrary.save_loaded_asset(sequence, False)
return sequence
# sequence movie
def render_sequence_to_movie(export_path, film_resolution, delay_every_frame, on_finished_callback):
# 1) Create an instance of our UAutomatedLevelSequenceCapture and override all of the settings on it. This class is currently
# set as a config class so settings will leak between the Unreal Sequencer Render-to-Movie UI and this object. To work around
# this, we set every setting via the script so that no changes the user has made via the UI will affect the script version.
# The users UI settings will be reset as an unfortunate side effect of this.
capture_settings = unreal.AutomatedLevelSequenceCapture()
# Set all POD settings on the UMovieSceneCapture
#capture_settings.settings.output_directory = unreal.DirectoryPath("../../../unreal-render-release/Saved/VideoCaptures1/")
capture_settings.settings.output_directory = unreal.DirectoryPath(export_path)
# If you game mode is implemented in Blueprint, load_asset(...) is going to return you the C++ type ('Blueprint') and not what the BP says it inherits from.
# Instead, because game_mode_override is a TSubclassOf we can use unreal.load_class to get the UClass which is implicitly convertable.
# ie: capture_settings.settings.game_mode_override = unreal.load_class(None, "/Game/AI/TestingSupport/AITestingGameMode.AITestingGameMode_C")
capture_settings.settings.game_mode_override = None
capture_settings.settings.output_format = "{camera}" + "_{frame}"
capture_settings.settings.overwrite_existing = False
capture_settings.settings.use_relative_frame_numbers = False
capture_settings.settings.handle_frames = 0
capture_settings.settings.zero_pad_frame_numbers = 4
# If you wish to override the output framerate you can use these two lines, otherwise the framerate will be derived from the sequence being rendered
capture_settings.settings.use_custom_frame_rate = False
# capture_settings.settings.custom_frame_rate = unreal.FrameRate(24,1)
capture_settings.settings.resolution.res_x = film_resolution
capture_settings.settings.resolution.res_y = film_resolution
capture_settings.settings.enable_texture_streaming = False
capture_settings.settings.cinematic_engine_scalability = True
capture_settings.settings.cinematic_mode = True
capture_settings.settings.allow_movement = False # Requires cinematic_mode = True
capture_settings.settings.allow_turning = False # Requires cinematic_mode = True
capture_settings.settings.show_player = False # Requires cinematic_mode = True
capture_settings.settings.show_hud = False # Requires cinematic_mode = True
capture_settings.use_separate_process = False
capture_settings.close_editor_when_capture_starts = False # Requires use_separate_process = True
capture_settings.additional_command_line_arguments = "-NOSCREENMESSAGES" # Requires use_separate_process = True
capture_settings.inherited_command_line_arguments = "" # Requires use_separate_process = True
# Set all the POD settings on UAutomatedLevelSequenceCapture
capture_settings.use_custom_start_frame = False # If False, the system will automatically calculate the start based on sequence content
capture_settings.use_custom_end_frame = False # If False, the system will automatically calculate the end based on sequence content
capture_settings.custom_start_frame = unreal.FrameNumber(0) # Requires use_custom_start_frame = True
capture_settings.custom_end_frame = unreal.FrameNumber(0) # Requires use_custom_end_frame = True
capture_settings.warm_up_frame_count = 0
capture_settings.delay_before_warm_up = 0
capture_settings.delay_before_shot_warm_up = 0
capture_settings.delay_every_frame = delay_every_frame
capture_settings.write_edit_decision_list = True
# Tell the capture settings which level sequence to render with these settings. The asset does not need to be loaded,
# as we're only capturing the path to it and when the PIE instance is created it will load the specified asset.
# If you only had a reference to the level sequence, you could use "unreal.SoftObjectPath(mysequence.get_path_name())"
capture_settings.level_sequence_asset = unreal.SoftObjectPath(sequence_asset_path)
# To configure the video output we need to tell the capture settings which capture protocol to use. The various supported
# capture protocols can be found by setting the Unreal Content Browser to "Engine C++ Classes" and filtering for "Protocol"
# ie: CompositionGraphCaptureProtocol, ImageSequenceProtocol_PNG, etc. Do note that some of the listed protocols are not intended
# to be used directly.
# Right click on a Protocol and use "Copy Reference" and then remove the extra formatting around it. ie:
# Class'/Script/MovieSceneCapture.ImageSequenceProtocol_PNG' gets transformed into "/Script/MovieSceneCapture.ImageSequenceProtocol_PNG"
capture_settings.set_image_capture_protocol_type(unreal.load_class(None, "/Script/MovieSceneCapture.ImageSequenceProtocol_PNG"))
# After we have set the capture protocol to a soft class path we can start editing the settings for the instance of the protocol that is internallyc reated.
capture_settings.get_image_capture_protocol().compression_quality = 85
# Finally invoke Sequencer's Render to Movie functionality. This will examine the specified settings object and either construct a new PIE instance to render in,
# or create and launch a new process (optionally shutting down your editor).
unreal.SequencerTools.render_movie(capture_settings, on_finished_callback)
# sequence end call back
def on_render_movie_finished(success):
unreal.log('on_render_movie_finished is called')
if success:
unreal.log('---end LevelSequenceTask')
# check asset is exist
def check_sequence_asset_exist():
sequence_path = os.path.join(root_dir, 'Content', 'Render_Sequence.uasset')
if(os.path.exists(sequence_path)):
unreal.log('---sequence is exist os path---')
os.remove(sequence_path)
else:
unreal.log('---sequence is not exist os path---')
if unreal.EditorAssetLibrary.does_asset_exist(sequence_asset_path):
unreal.log('---sequence dose asset exist---')
unreal.EditorAssetLibrary.delete_asset(sequence_asset_path)
else:
unreal.log('---sequence does not asset exist---')
sequence_asset_data = unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path(sequence_asset_path)
if sequence_asset_data:
unreal.log('---sequence is exist on content---')
sequence_asset = unreal.AssetData.get_asset(sequence_asset_data)
unreal.EditorAssetLibrary.delete_loaded_asset(sequence_asset)
else:
unreal.log('---sequence is not exit on content---')
if __name__ == "__main__":
# bind event
on_finished_callback = unreal.OnRenderMovieStopped()
on_finished_callback.bind_callable(on_render_movie_finished)
# get json file
root_dir = unreal.SystemLibrary.get_project_directory()
json_path = os.path.join(root_dir, 'RawData', 'export_json_for_unreal.json')
# get json data
camera_transform_array, camera_rotation_array, camera_name_array, export_path, film_fov, film_resolution, delay_every_frame = read_json_file(json_path)
# check sequence is asset
check_sequence_asset_exist()
# create sequence
create_sequence('Render_Sequence', camera_transform_array, camera_rotation_array, camera_name_array, film_fov)
# run sequence
render_sequence_to_movie(export_path, film_resolution, delay_every_frame, on_finished_callback)
export_json_for_unreal.json
{
"film_fov": 100,
"film_resolution": 1680,
"delay_every_frame": 1.5,
"output_image_path": "E:/result",
"camera_data": {
"camera_qRrq9DqY": {
"type": "Camera",
"relative_location": [
328.3489685058594,
-148.0,
169.689208984375
],
"relative_rotation": [
-90.0,
-39.49755875696428,
0.0
]
},
"camera_yYxbYjuf": {
"type": "Camera",
"relative_location": [
-467.9044494628906,
-148.0,
-95.96395874023438
],
"relative_rotation": [
-90.0,
38.32306260219775,
0.0
]
}
}
}
在编辑器Python命令行下执行脚本,将在Content目录下创建Render_Sequence,然后自动进入拍照,并导出到指定目录下