BlenderのコンポジターでマルチレイヤーEXRファイルを回転させるにはどうすればよいですか?
画像を回転させるのは簡単な作業のように思えますが、BlenderでマルチレイヤーEXR形式のファイルを操作するときにこの問題に直面します。
しかし、なぜ?
archvisシーンをアニメーションとして設定すると、さまざまな静止画のオブジェクトを変更したり、すべてのカメラアングルを一度にアニメーションとしてレンダリングしたりできるので便利です。同じインテリアの横向きと縦向きの画像が必要な場合があり、小さなスクリプトを使用してXとYの解像度の寸法をすばやく切り替え、フレーミングが完了したらカメラを回転させて、一部のフレームが回転してレンダリングされるようにします。そうすれば、一連の画像を合成するのに非常に便利です。
最近、シーンをいくつかのバッチでレンダリングし、1つの8Kパノラマ画像、1つを回転させた横向きの5つの4Kリビングルーム画像、すべて縦向きの階段の5つの画像をすべて順番にレンダリングしました。合成方向と解像度の違いにもかかわらず、すべての画像を1つのシーケンスとして一度に実行し、ファイル出力ノードを使用して、その1つのシーケンスから正しい解像度と方向でファイルに出力できます。だから今私は知っています、私は蓄積している同じシーケンスで異なる方向を持つことができます。
これまでのように出力だけを回転させるのではなく、合成中に正しく回転させたすべての画像を常に見ることができれば、さらに素晴らしいでしょう。
ファイルのシーケンスでローテーションが必要な、レンダリングされたすべてのマルチレイヤーEXRファイルをローテーションする必要があります。
私が試したこと
マルチレイヤーEXRのすべての出力をPythonでファイル出力ノードに接続し、コンポジターノードを挿入してそれらを自動的に編集するのに問題はないので、1つのレイヤーで機能するソリューションは便利ですが、コンポジターで画像を回転させることはできません回転すると画像が元のサイズにトリミングされるため:
質問
マルチレイヤーEXRのすべてのレイヤーを回転させ、BlenderのCompositorノードを使用して回転した寸法の画像に出力することは可能ですか?
以下に詳述されている私の解決策と回答には、Cryptomatteに問題があります。私はそれらの問題のない解決策を見つけることを期待して賞金を始めています。私はPythonを使った解決策を考え始めていますが、Blenderからどのようにシンプルで使いやすいのかわかりません...
回答
クリプトマットについて
この問題は回転によるものではなく、ノード上のアルファによるものです。
これにより、無地の画像になります。
これでノードグループを変更した場合:
これにより、マスクからの画像サイズを考慮せずに(0を掛けて)保持できますが、回転したクリプトマット値が追加されます。
簡単なテストケースファイル:
このソリューションには問題があります-答えの終わりを参照してください。
ブロックマンが指摘したように、私が見つけられなかった回転画像について非常によく似た質問があります。コンポジターの画像の寸法は、何かと混合された場合、最初のノードから継承されることを説明しています。マスクが選択されていないマスクノードは、寸法を完全に設定するために機能します。
したがって、マルチレイヤーEXRファイルを回転させるには、すべてのレイヤーを回転させる必要があります。Pythonを使用すると、小さなアドオン内でパネルを作成すると、次のようになります。
bl_info = {
"name": "Nodes",
"author": "Martynas Žiemys",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "Compositor -> n panel -> Node Tools",
"description": "",
"warning": "",
"doc_url": "",
"category": "Compositor",
}
import bpy
from bpy.types import Panel, Operator
from mathutils import Vector
from math import radians
class OutputRotatedMultilayer(Operator):
"""Make multilayer EXR file output for all outputs of active image node rotated"""
bl_idname = "node.multilayer_output_rotated"
bl_label = "Multilayer Output Rotated"
@classmethod
def poll(cls, context):
image_node_active = False
if context.scene.node_tree.nodes.active is not None:
image_node_active = (context.scene.node_tree.nodes.active.type in {"IMAGE","R_LAYERS"})
return image_node_active
def execute(self, context):
if "RotateImage" not in bpy.data.node_groups:
rotate = bpy.data.node_groups.new('RotateImage', 'CompositorNodeTree')
rotate.inputs.new('NodeSocketColor','Image')
rotate.outputs.new('NodeSocketColor','Image')
out = rotate.nodes.new('NodeGroupOutput')
out.location = (200,0)
input = rotate.nodes.new('NodeGroupInput')
input.location = (-400,0)
mask = rotate.nodes.new('CompositorNodeMask')
mask.location = (-200,200)
mask.use_feather = 0
mask.size_source = 'FIXED'
mask.size_x = 3600
mask.size_y = 2400
mix = rotate.nodes.new('CompositorNodeAlphaOver')
mix.location = (0,25)
mix.hide = 1
rot = rotate.nodes.new('CompositorNodeRotate')
rot.location = (-200,-50)
rot.filter_type = 'NEAREST'
rot.inputs[1].default_value= radians(90)
rotate.links.new(out.inputs[0], mix.outputs[0])
rotate.links.new(rot.inputs[0], input.outputs[0])
rotate.links.new(mix.inputs[1], mask.outputs[0])
rotate.links.new(mix.inputs[2], rot.outputs[0])
width = context.scene.node_tree.nodes.active.width
active = context.scene.node_tree.nodes.active
tree = context.scene.node_tree
links = tree.links
output = tree.nodes.new('CompositorNodeOutputFile')
output.location = active.location + Vector((500,0))
output.format.file_format = 'OPEN_EXR_MULTILAYER'
output.format.color_depth = '32'
output.format.color_mode = 'RGBA'
output.format.compression = 15
output.layer_slots.clear()
for i,every_slot in enumerate(active.outputs):
if active.type == "R_LAYERS":
if every_slot.enabled:
output.layer_slots.new( name = every_slot.name )
g = tree.nodes.new('CompositorNodeGroup')
g.node_tree = bpy.data.node_groups["RotateImage"]
g.hide = 1
g.location = (-100,i*50)
links.new(active.outputs[i], g.inputs[0])
links.new(g.outputs[0], output.inputs[every_slot.name])
else:
output.layer_slots.new( name = every_slot.name )
g = tree.nodes.new('CompositorNodeGroup')
g.node_tree = bpy.data.node_groups["RotateImage"]
g.hide = 1
g.location = active.location + Vector((200,i*-33))
links.new(active.outputs[i], g.inputs[0])
links.new(g.outputs[0], output.inputs[every_slot.name])
return {'FINISHED'}
class NODE_PT_node_tools(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node Tools"
bl_label = "Node Tools"
@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'NODE_EDITOR'
def draw(self, context):
layout = self.layout
col = layout.column()
col.operator("node.multilayer_output_rotated")
def register():
bpy.utils.register_class(OutputRotatedMultilayer)
bpy.utils.register_class(NODE_PT_node_tools)
def unregister():
bpy.utils.unregister_class(OutputRotatedMultilayer)
bpy.utils.unregister_class(NODE_PT_node_tools)
if __name__ == "__main__":
register()
これはCryptomatteでは機能しないことが判明しました
回転プロセスにより、クリプトマット値の精度が台無しになり、マスクエッジが正しくなくなります。
レンダリング結果はEXRの寸法や方向を考慮していないため、答えは達成したい結果によって異なります。簡単な方法は、EXRがたとえば1920 x 1080の場合、レンダリングサイズを1920 x 1920に設定することです。この方法では、画像は常にフィットし、どちらの方法でもトリミングされません。おそらく50%のような異なるパーセンテージでレンダリングしたい場合は、[ノードの回転]を[レンダリングサイズ]> [フィット]に設定した後、スケールノードを配置する必要があります。
レンダリングされた画像のアスペクト比を16:9に維持したい場合は、次の2つの方法があります。
- EXRは、可能な限り多くのスペースを埋める必要があります(0°で1920 x 1080、90°で608 x 1080)または
- 寸法は同じままである必要があります(0°で1080 x 608、90°で608 x 1080)。
どちらの方法でも、レンダリングサイズ>フィットで上記のスケールノードを必要とし、2番目のスケールノードを相対> X = Y = 9/16 = 0.5625に設定する必要があります。
オプション1の場合、相対ノードを無効にするか、回転が0°の場合はX = Y = 1に設定する必要があります。90°または-90°の場合、X = Y = 0.5625で有効にする必要があります。
オプション2の場合、0°または90°に関係なく有効のままにしますが、前述のように、この場合、EXRは画像を水平方向に塗りつぶしません。
回転値に応じて相対ノードを自動的に無効にするノード設定を作成しました。画像を参照してください。シーケンスをレンダリングして回転のキーフレームを設定する場合は、ノードを手動で無効にする必要はありません。レンダリング寸法のアスペクト比は、ドライバーによって計算されます。