サルでもわかるBlender

2026/2/4

AIテクスチャをBlenderに秒速で貼る!Pythonで一発セット&微調整

Blenderでテクスチャを貼る作業、意外と手順が多くて大変ですよね。

今回は、AIでテクスチャを作り、BlenderではPythonで一発セットする方法を紹介します。最後にUVを少し調整して、正面だけに自然に貼れるところまで再現します。

▼ドアのテクスチャ生成プロンプト(動画 00:39~)
▼Pythonコード(動画 02:00~)

目次

①AIで「扉テクスチャ」を作る

まずはBlenderに貼るための「扉の画像」をAIで用意します。
この段階でやることはシンプルで、画像を2枚用意するだけです。

  • ①メイン画像(色の画像)
  • ②ノーマルマップ(凹凸の画像)

画面①:扉の画像(メイン)を生成する

Nano Banana Pro(Gemini)で、扉の画像を生成します。

同じ内容で作りたい場合は、下のプロンプトをそのままコピペしてください。

ドアのテクスチャ生成プロンプト(動画 00:39~)

プロンプト
Create a square 1:1 (2048x2048) texture-style image intended for Blender PBR basecolor/albedo use.

Scene / Subject
- A flat mid-gray wall with a single white interior door centered.
- Door: simple white door with subtle, minimal classic panel molding (clean and understated; not ornate).
- Add a round doorknob on the right side at standard height.
- Knob material: matte/satin neutral metal, not shiny.

Composition / Camera
- Front view, orthographic projection, perfectly centered and symmetrical.
- Full door visible. Straight vertical/horizontal lines.
- No perspective distortion, no depth of field, no vignette, no lens distortion, no grain.

Lighting / Look (albedo-friendly)
- Flat, neutral, evenly lit, diffuse only.
- No cast shadows, no highlights, no reflections, no bloom, no ambient occlusion.
- Clean minimal "texture reference / studio scan" look: high detail but low contrast.
- Sharp, clean edges.

Background constraints
- Plain gray wall, smooth plaster.
- No objects, no switches, no baseboards, no floor/ceiling, no text, no logos.

Safety / Originality requirement (important)
- Do not replicate any copyrighted, branded, or recognizable real-world door/product designs.
- Generate an original, generic door design with common, non-distinctive features.
- Avoid distinctive patterns, trademarks, or unique signature shapes.

Negative prompts
- angled view, dramatic lighting, cast shadows, specular glare, strong contrast, tilt, clutter, decorations,
- dirt, stains, cracks, text, logos, watermarks

画面②:ノーマルマップを生成する

次に、同じ扉に凹凸(立体感)を付けるためのノーマルマップを作ります。

AIには、たとえば「この扉画像のノーマルマップを作って」のように指示します。

  • ノーマルマップは「凹凸の情報」の画像です
  • この2枚がそろうと、Blenderで立体感が出せます

画面③:2枚をダウンロードしてPCに保存する

メイン画像とノーマルマップの2枚ができたら、ダウンロードしてPCに保存します。

画面④:ファイル名の末尾をそろえる(超重要)

このあとBlenderのPythonが自動判別しやすいように、ファイル名の末尾をそろえます。

  • メイン画像:末尾を 「_albedo」
  • ノーマルマップ:末尾を 「_normal」

たとえば扉なら、こんな感じにします。

うまくいかない時(ここだけ見てOK)

  • Blenderで自動判別されない → 末尾が _albedo / _normal になってる?(スペル違いに注意)
  • ノーマルが作れない → 「ノーマルマップ」「normal map」の言い方で試す

②Blenderにテクスチャを貼る(Pythonで一発)

ここからBlenderです。
手作業だと「ノード追加→接続→設定」が大変ですが、Pythonで自動で一発セットします。

画面①:貼りたいオブジェクトを選ぶ

まず、テクスチャを貼りたいオブジェクト(例:キューブ)をクリックして選択します。

  • 選択できていないと、実行しても何も起きません

画面②:Scriptingタブを開く

上のタブから Scripting を開きます。

画面③:新規でスクリプト欄を作る

新規 を押して、コードを貼る場所を作ります。

画面④:コードを貼って「実行」

下のPythonコードを貼り付けて、実行(Run Script) を押します。

貼り付けるPythonコード(動画 02:00~)

Python
import bpy
import os

IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".tif", ".tiff", ".exr", ".bmp", ".tga", ".webp"}

def _build_material(obj, base_color_path: str, normal_map_path: str):
    mat = bpy.data.materials.get("AI_Door_Mat")
    if mat is None:
        mat = bpy.data.materials.new("AI_Door_Mat")
    mat.use_nodes = True

    if obj.data.materials:
        obj.data.materials[0] = mat
    else:
        obj.data.materials.append(mat)

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

    # 既存ノードを全消し(残したい場合はこのブロックをコメントアウト)
    for n in list(nodes):
        nodes.remove(n)

    out = nodes.new("ShaderNodeOutputMaterial")
    out.location = (500, 0)

    bsdf = nodes.new("ShaderNodeBsdfPrincipled")
    bsdf.location = (200, 0)

    texcoord = nodes.new("ShaderNodeTexCoord")
    texcoord.location = (-800, 0)

    mapping = nodes.new("ShaderNodeMapping")
    mapping.location = (-600, 0)

    tex_base = nodes.new("ShaderNodeTexImage")
    tex_base.location = (-350, 150)
    tex_base.label = "Base Color (Albedo)"

    tex_normal = nodes.new("ShaderNodeTexImage")
    tex_normal.location = (-350, -150)
    tex_normal.label = "Normal Map"

    normal_map = nodes.new("ShaderNodeNormalMap")
    normal_map.location = (-80, -150)

    img_base = bpy.data.images.load(base_color_path, check_existing=True)
    img_norm = bpy.data.images.load(normal_map_path, check_existing=True)

    tex_base.image = img_base
    tex_normal.image = img_norm

    # ノーマルは Non-Color(超重要)
    try:
        tex_normal.image.colorspace_settings.name = "Non-Color"
    except:
        pass

    links.new(texcoord.outputs["UV"], mapping.inputs["Vector"])
    links.new(mapping.outputs["Vector"], tex_base.inputs["Vector"])
    links.new(mapping.outputs["Vector"], tex_normal.inputs["Vector"])

    links.new(tex_base.outputs["Color"], bsdf.inputs["Base Color"])

    links.new(tex_normal.outputs["Color"], normal_map.inputs["Color"])
    links.new(normal_map.outputs["Normal"], bsdf.inputs["Normal"])

    links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])

    normal_map.inputs["Strength"].default_value = 1.0


class AIDOOR_OT_setup_from_files(bpy.types.Operator):
    bl_idname = "aidoor.setup_from_files"
    bl_label = "読み込む"
    bl_options = {'REGISTER', 'UNDO'}

    # ✅ 複数ファイル選択を受け取る
    directory: bpy.props.StringProperty(subtype="DIR_PATH")
    files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement)

    # ファイルブラウザで画像だけ見せたい(任意)
    filter_glob: bpy.props.StringProperty(default="*.png;*.jpg;*.jpeg;*.tif;*.tiff;*.exr;*.bmp;*.tga;*.webp", options={'HIDDEN'})

    def invoke(self, context, event):
        if context.active_object is None:
            self.report({'ERROR'}, "アクティブオブジェクトがありません。キューブ等を選択してから実行してください。")
            return {'CANCELLED'}

        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        # 選択されたファイルのフルパスを作る
        selected_paths = []
        if self.files:
            for f in self.files:
                p = os.path.join(self.directory, f.name)
                selected_paths.append(p)

        if not selected_paths:
            self.report({'ERROR'}, "ファイルが選択されていません。_albedo と _normal の2枚を複数選択してください。")
            return {'CANCELLED'}

        # _albedo / _normal を判別
        albedo_path = None
        normal_path = None

        for p in selected_paths:
            base = os.path.basename(p)
            stem, ext = os.path.splitext(base)
            if ext.lower() not in IMAGE_EXTS:
                continue

            s = stem.lower()
            if s.endswith("_albedo") and albedo_path is None:
                albedo_path = p
            if s.endswith("_normal") and normal_path is None:
                normal_path = p

        # 必須チェック
        if not albedo_path or not normal_path:
            picked = ", ".join([os.path.basename(x) for x in selected_paths])
            self.report(
                {'ERROR'},
                "判別に失敗しました。\n"
                "選んだファイル名の末尾が `_albedo` と `_normal` になっている必要があります。\n"
                f"選択: {picked}"
            )
            return {'CANCELLED'}

        obj = context.active_object
        _build_material(obj, albedo_path, normal_path)

        self.report(
            {'INFO'},
            f"✅ セット完了\nAlbedo: {os.path.basename(albedo_path)}\nNormal: {os.path.basename(normal_path)}"
        )
        return {'FINISHED'}


def _safe_unregister(cls):
    try:
        bpy.utils.unregister_class(cls)
    except:
        pass


def register():
    _safe_unregister(AIDOOR_OT_setup_from_files)
    bpy.utils.register_class(AIDOOR_OT_setup_from_files)


if __name__ == "__main__":
    register()
    # ✅ Run Script直後にファイル選択ダイアログを開く
    bpy.ops.aidoor.setup_from_files('INVOKE_DEFAULT')

画面⑤:画像を2枚選ぶ(_albedo / _normal)

実行すると、画像を選ぶ画面が出ます。

保存しておいた _albedo_normal の2枚を選んで読み込みます。

  • ファイル名の末尾が _albedo / _normal だと自動判別しやすいです

画面⑥:Shadingで結果を確認

Shading タブを開きます。
ノードが自動で組まれて、テクスチャも貼られた状態になっていれば成功です。

(必要なら)凹凸を強くする

凹凸感を強くしたい場合は、「ノーマルマップ」ノードの「強さ」を上げます。

うまくいかない時(ここだけ見てOK)

  • 何も起きない → 最初にオブジェクト選択できてる?
  • テクスチャが反映されない → _albedo と _normalの2枚を選べてる?
  • ノーマルが変に見える → 「強さ」を下げて様子を見る

③テクスチャを整える(UV調整)

最後に、扉の絵が正面だけに出るようにUVを整えます。
この作業をしないと、扉の絵が横や上の面にも貼られてしまって不自然になります。

画面①:UV Editingを開く

上のタブから UV Editing を開きます。

  • 右:3D画面(立体の編集)
  • 左:UV画面(テクスチャ上の位置)

画面②:正面の「面」を選ぶ

扉を見せたい正面の面を選択します。

(選ばれている面がハイライト表示になっていればOKです)

画面③:UVを扉の位置に合わせる

左側のUV画面で、正面のUVを「扉の絵」にぴったり合わせます。

  • G:移動(扉の位置に合わせる)
  • S:拡大・縮小(扉のサイズに合わせる)
  • R:回転(向きを合わせる)

画面④:他の面は「余白」へ逃がす

扉を表示したくない面は、UVを小さくしてテクスチャの余白へ移動します。
これで、横や上の面に扉が映らなくなります。

  1. 正面以外の面を選ぶ
  2. UV画面で S で小さくする
  3. Gで余白へ移動する

これで「箱の正面だけに扉が自然に貼られた状態」になります。

うまくいかない時(ここだけ見てOK)

  • 扉が動かない → Edit Modeになってる? 面選択になってる?
  • UVが見えない → 正面の面が選択できてる?
  • 別の面に扉が出る → その面のUVが扉の上に残ってない?(余白へ逃がす)