見出し画像

Blender、pythonでR、G、Bの3色の割合をミックスシェーダーでコントロールできるマテリアルを作成します。

"""
タイトル: RGB比率コントロール可能なマテリアルアニメーションスクリプト
説明:
Blender内でR(赤)、G(緑)、B(青)の3色を使用し、
それぞれの割合をミックスシェーダーでコントロール可能なマテリアルを作成します。
色の切り替えと比率の変更はアニメーションとして設定されます。

作成日時: 2024年11月14日
バージョン: 1.0.0
"""

import bpy


def setup_scene():
    """カメラとライト、既存のオブジェクトを初期化"""
    # 既存のオブジェクトを削除
    for obj in bpy.data.objects:
        bpy.data.objects.remove(obj, do_unlink=True)

    # 既存のマテリアルを削除
    for mat in bpy.data.materials:
        bpy.data.materials.remove(mat, do_unlink=True)

    # カメラを追加
    bpy.ops.object.camera_add(location=(0, -10, 5), rotation=(1.1, 0, 0))
    camera = bpy.context.object
    camera.name = "Camera"

    # ライトを追加
    bpy.ops.object.light_add(type='POINT', location=(5, -5, 10))
    light = bpy.context.object
    light.name = "Light"


def create_rgb_mixed_material(name):
    """
    R, G, Bの3色をミックスするマテリアルを作成します。

    Args:
        name (str): マテリアル名。

    Returns:
        bpy.types.Material: 作成したマテリアル。
    """
    mat = bpy.data.materials.get(name)
    if mat is None:
        mat = bpy.data.materials.new(name=name)
        mat.use_nodes = True

        nodes = mat.node_tree.nodes
        links = mat.node_tree.links

        # ノードのクリア
        for node in nodes:
            nodes.remove(node)

        # ノードの追加
        output_node = nodes.new(type="ShaderNodeOutputMaterial")
        mix_shader_1 = nodes.new(type="ShaderNodeMixShader")
        mix_shader_2 = nodes.new(type="ShaderNodeMixShader")
        diffuse_r = nodes.new(type="ShaderNodeBsdfDiffuse")
        diffuse_g = nodes.new(type="ShaderNodeBsdfDiffuse")
        diffuse_b = nodes.new(type="ShaderNodeBsdfDiffuse")

        # 各色用のDiffuseノードを設定
        diffuse_r.inputs["Color"].default_value = (1.0, 0.0, 0.0, 1.0)  # 赤
        diffuse_g.inputs["Color"].default_value = (0.0, 1.0, 0.0, 1.0)  # 緑
        diffuse_b.inputs["Color"].default_value = (0.0, 0.0, 1.0, 1.0)  # 青

        # ミックスシェーダーを構成
        links.new(diffuse_r.outputs["BSDF"], mix_shader_1.inputs[1])
        links.new(diffuse_g.outputs["BSDF"], mix_shader_1.inputs[2])
        links.new(mix_shader_1.outputs["Shader"], mix_shader_2.inputs[1])
        links.new(diffuse_b.outputs["BSDF"], mix_shader_2.inputs[2])
        links.new(mix_shader_2.outputs["Shader"], output_node.inputs["Surface"])

        # ファクター用ノードを追加
        factor_node_1 = nodes.new(type="ShaderNodeValue")
        factor_node_2 = nodes.new(type="ShaderNodeValue")

        factor_node_1.outputs[0].default_value = 0.5  # 最初は赤と緑のミックス
        factor_node_2.outputs[0].default_value = 0.5  # 最初はミックスと青のミックス

        links.new(factor_node_1.outputs[0], mix_shader_1.inputs["Fac"])
        links.new(factor_node_2.outputs[0], mix_shader_2.inputs["Fac"])

        # ファクターノードを保持
        mat["factor_nodes"] = {
            "Mix1": factor_node_1.name,
            "Mix2": factor_node_2.name,
        }

    return mat


def animate_rgb_material(mat, frame_start, frame_interval, color_sequence):
    """
    マテリアルのRGB比率をアニメーション化します。

    Args:
        mat (bpy.types.Material): ミックスされたマテリアル。
        frame_start (int): アニメーション開始フレーム。
        frame_interval (int): 各切り替え間隔のフレーム数。
        color_sequence (list): 色の比率のリスト [(R, G, B), ...]。
    """
    factor_nodes = mat.get("factor_nodes")
    if not factor_nodes:
        print("Factor nodes not found in material.")
        return

    factor_node_1 = mat.node_tree.nodes.get(factor_nodes["Mix1"])
    factor_node_2 = mat.node_tree.nodes.get(factor_nodes["Mix2"])

    if not factor_node_1 or not factor_node_2:
        print("Factor nodes not found in material nodes.")
        return

    frame = frame_start
    for color_ratio in color_sequence:
        r, g, b = color_ratio

        # 計算された比率を設定
        factor_node_1.outputs[0].default_value = r / (r + g) if (r + g) != 0 else 0.5
        factor_node_2.outputs[0].default_value = (r + g) / (r + g + b) if (r + g + b) != 0 else 0.5

        # アニメーションキーを挿入
        factor_node_1.outputs[0].keyframe_insert(data_path="default_value", frame=frame)
        factor_node_2.outputs[0].keyframe_insert(data_path="default_value", frame=frame)

        # 次のフレームに進む
        frame += frame_interval


def main():
    # シーンのセットアップ
    setup_scene()

    # 球体を作成
    bpy.ops.mesh.primitive_uv_sphere_add(radius=1, location=(0, 0, 1))
    sphere = bpy.context.object
    bpy.ops.object.shade_smooth()
    sphere.name = "Sphere"

    # マテリアルを作成して球体に適用
    rgb_material = create_rgb_mixed_material("RGBMaterial")
    sphere.data.materials.append(rgb_material)

    # アニメーション設定
    frame_start = 1
    frame_interval = 30
    color_sequence = [
        (1.0, 0.0, 0.0),  # 赤
        (0.0, 1.0, 0.0),  # 緑
        (0.0, 0.0, 1.0),  # 青
        (1.0, 1.0, 0.0),  # 黄色(赤と緑の混合)
        (0.0, 1.0, 1.0),  # シアン(緑と青の混合)
        (1.0, 0.0, 1.0)   # マゼンタ(赤と青の混合)
    ]
    animate_rgb_material(rgb_material, frame_start, frame_interval, color_sequence)

    # 初期フレームを設定
    bpy.context.scene.frame_set(1)


# 実行
main()

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