見出し画像

Blenderで球体の中にHDR画像を貼り付けて、VRMとのアニメーションを作成

以下のような背景のあるアニメーションを作成する手順を紹介する。
(この手順で出力される背景は、3D的なオブジェクトではないので、複雑なことはできません。)


HDR画像を作成

以下の手順を参考に、HDR画像を作成する。

内側にHDR画像を貼り付けた球体を出力

以下のスクリプトをBlenderのスクリプトタブで実行する。
(実行手順も上記のQiita記事を参考にしてください。)

import bpy
import math

def create_hdri_sphere(hdri_path):
    # 既存のオブジェクトをすべて削除
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()

    # 世界の背景設定
    world = bpy.context.scene.world
    world.use_nodes = True
    node_tree = world.node_tree
    node_tree.nodes.clear()

    # テクスチャノードを追加
    tex_node = node_tree.nodes.new(type='ShaderNodeTexEnvironment')
    tex_node.image = bpy.data.images.load(hdri_path)
    
    # 背景ノードを追加
    background_node = node_tree.nodes.new(type='ShaderNodeBackground')
    
    # ワールドアウトプットノードを追加
    output_node = node_tree.nodes.new(type='ShaderNodeOutputWorld')
    
    # ノードを接続
    node_tree.links.new(tex_node.outputs['Color'], background_node.inputs['Color'])
    node_tree.links.new(background_node.outputs['Background'], output_node.inputs['Surface'])

    # 球体を作成
    bpy.ops.mesh.primitive_uv_sphere_add(segments=64, ring_count=32, radius=10)
    sphere = bpy.context.active_object

    # マテリアルを作成
    mat = bpy.data.materials.new(name="HDRI_Material")
    mat.use_nodes = True
    sphere.data.materials.append(mat)

    # ノードを設定
    nodes = mat.node_tree.nodes
    nodes.clear()

    # テクスチャノードを追加
    tex_node = nodes.new(type='ShaderNodeTexEnvironment')
    tex_node.image = bpy.data.images.load(hdri_path)

    # エミッションノードを追加
    emission_node = nodes.new(type='ShaderNodeEmission')

    # マテリアルアウトプットノードを追加
    output_node = nodes.new(type='ShaderNodeOutputMaterial')

    # ノードを接続
    links = mat.node_tree.links
    links.new(tex_node.outputs['Color'], emission_node.inputs['Color'])
    links.new(emission_node.outputs['Emission'], output_node.inputs['Surface'])

    # UVマッピングを調整
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.uv.sphere_project()
    bpy.ops.object.mode_set(mode='OBJECT')

# HDRIファイルのパスを指定して関数を呼び出す
hdri_path = r"C:\Users\XXXXX\AAAAAA.hdr"
create_hdri_sphere(hdri_path)

出力される球体の内側。

出力される球体の外側。(わかりにくいで、下の画像の球体の内側は上の画像のような状態)

BlenderにVRMをインポート

※こちらは以下の記事を参考にしてください。

VRMの場所を修正

上記の手順通りにVRMをインポートすると、微妙な位置にモデルが出力されるため、以下のスクリプトをBlenderで実行し、モデルの位置とサイズを調整する。

import bpy

# アクティブなオブジェクト(選択されているVRMモデル)を取得
obj = bpy.context.active_object

if obj is not None:
    # スケールを5に設定
    obj.scale = (5, 5, 5)
    
    # Z軸の位置を-10に設定
    obj.location.z = -10
    
    print(f"オブジェクト '{obj.name}' のスケールと位置を更新しました。")
else:
    print("選択されているオブジェクトがありません。VRMモデルを選択してからスクリプトを実行してください。")

(必要に応じて)モーションをインポート

以下の手順などで作成したVRMAをBlenderにインポートする。


アニメーションを作成

以下のスクリプトを実行し、アニメーションを作成する。(PCのスペック次第で、そこそこ時間がかかります。)

■モデルを斜め上から

import bpy
import math
import os
from datetime import datetime

# ユーザーが調整可能なパラメータ
distance_from_model = 9.0       # モデルからの距離
camera_height = -2.0            # カメラの高さ
camera_angle = 80.0             # カメラの角度(度単位)
output_path = r"C:\Users\XXXXX\output\"

# 解像度の指定
resolution_x = 1080             # 横解像度
resolution_y = 1920             # 縦解像度

# フレームレートと回転角度の指定
frame_rate = 30                 # フレームレート(fps)
rotation_degrees = 360          # カメラの回転角度(度単位)

# 既存のカメラを削除
for obj in bpy.data.objects:
    if obj.type == 'CAMERA':
        bpy.data.objects.remove(obj, do_unlink=True)

# 新しいカメラを追加
bpy.ops.object.camera_add()
camera = bpy.context.object
camera.name = "RotatingCamera"

# カメラの角度をラジアンに変換
camera_angle_rad = math.radians(camera_angle)

# カメラの初期位置を設定
camera.location = (distance_from_model, 0, camera_height)
camera.rotation_euler = (camera_angle_rad, 0, math.pi / 2)

# 回転の中心となる空オブジェクトを原点に追加
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0, 0, 0))
pivot = bpy.context.object
pivot.name = "CameraPivot"

# カメラをピボットにペアレント設定
camera.parent = pivot

# シーンの設定
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 300  # アニメーションのフレーム数(例として10秒間のアニメーション)

# フレームレートを設定
scene.render.fps = frame_rate

# カメラを回転させるためのキーを設定
pivot.rotation_euler = (0, 0, 0)
pivot.keyframe_insert(data_path="rotation_euler", frame=scene.frame_start)

# 回転角度をラジアンに変換
rotation_radians = math.radians(rotation_degrees)
pivot.rotation_euler = (0, 0, rotation_radians)
pivot.keyframe_insert(data_path="rotation_euler", frame=scene.frame_end)

# 補間を線形に設定
for fcurve in pivot.animation_data.action.fcurves:
    for keyframe in fcurve.keyframe_points:
        keyframe.interpolation = 'LINEAR'

# タイムスタンプを生成
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

# レンダリング出力設定
scene.render.image_settings.file_format = 'FFMPEG'
scene.render.ffmpeg.format = 'MPEG4'
scene.render.filepath = os.path.join(output_path, f"animation_{timestamp}.mp4")

# 解像度を設定
scene.render.resolution_x = resolution_x
scene.render.resolution_y = resolution_y
scene.render.resolution_percentage = 100  # 解像度の割合(100%で指定した解像度)

# 出力ディレクトリが存在しない場合は作成
if not os.path.exists(output_path):
    os.makedirs(output_path)

# カメラをアクティブカメラに設定
scene.camera = camera

# アニメーションをレンダリング
bpy.ops.render.render(animation=True)

■モデルを斜め下から

import bpy
import math
import os
from datetime import datetime

# ユーザーが調整可能なパラメータ
distance_from_model = 6.0       # モデルからの距離
camera_height = -7.0            # カメラの高さ
camera_angle = 110.0             # カメラの角度(度単位)
output_path = r"C:\Users\XXXXX\output\"

# 解像度の指定
resolution_x = 1080             # 横解像度
resolution_y = 1920             # 縦解像度

# フレームレートと回転角度の指定
frame_rate = 30                 # フレームレート(fps)
rotation_degrees = 360          # カメラの回転角度(度単位)

# 既存のカメラを削除
for obj in bpy.data.objects:
    if obj.type == 'CAMERA':
        bpy.data.objects.remove(obj, do_unlink=True)

# 新しいカメラを追加
bpy.ops.object.camera_add()
camera = bpy.context.object
camera.name = "RotatingCamera"

# カメラの角度をラジアンに変換
camera_angle_rad = math.radians(camera_angle)

# カメラの初期位置を設定
camera.location = (distance_from_model, 0, camera_height)
camera.rotation_euler = (camera_angle_rad, 0, math.pi / 2)

# 回転の中心となる空オブジェクトを原点に追加
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0, 0, 0))
pivot = bpy.context.object
pivot.name = "CameraPivot"

# カメラをピボットにペアレント設定
camera.parent = pivot

# シーンの設定
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 300  # アニメーションのフレーム数(例として10秒間のアニメーション)

# フレームレートを設定
scene.render.fps = frame_rate

# カメラを回転させるためのキーを設定
pivot.rotation_euler = (0, 0, 0)
pivot.keyframe_insert(data_path="rotation_euler", frame=scene.frame_start)

# 回転角度をラジアンに変換
rotation_radians = math.radians(rotation_degrees)
pivot.rotation_euler = (0, 0, rotation_radians)
pivot.keyframe_insert(data_path="rotation_euler", frame=scene.frame_end)

# 補間を線形に設定
for fcurve in pivot.animation_data.action.fcurves:
    for keyframe in fcurve.keyframe_points:
        keyframe.interpolation = 'LINEAR'

# タイムスタンプを生成
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

# レンダリング出力設定
scene.render.image_settings.file_format = 'FFMPEG'
scene.render.ffmpeg.format = 'MPEG4'
scene.render.filepath = os.path.join(output_path, f"animation_{timestamp}.mp4")

# 解像度を設定
scene.render.resolution_x = resolution_x
scene.render.resolution_y = resolution_y
scene.render.resolution_percentage = 100  # 解像度の割合(100%で指定した解像度)

# 出力ディレクトリが存在しない場合は作成
if not os.path.exists(output_path):
    os.makedirs(output_path)

# カメラをアクティブカメラに設定
scene.camera = camera

# アニメーションをレンダリング
bpy.ops.render.render(animation=True)

アウトプット

以下のようなアニメーションが作成される。

■モデルを斜め上から(速度だけいじってます。)

■モデルを斜め下から

+α

AnimateDiffを使うと、3D感が緩和できます。



いいなと思ったら応援しよう!