ラベル Blenderアドオン の投稿を表示しています。 すべての投稿を表示
ラベル Blenderアドオン の投稿を表示しています。 すべての投稿を表示

2026年7月8日水曜日

自然言語によるBlenderマテリアル設定 自作Blenderアドオンの使い勝手 その3

 Configuring Blender Materials via Natural Language: Usability of a Custom Blender Add-on (Part 3)


I tested a custom Blender add-on that allows for material configuration using natural language. By using natural language, it is possible to construct complex node setups and create materials suitable for practical use.


自作Blenderアドオンについて、自然言語によるマテリアル設定をテストしました。自然言語により、複雑なノードを組んで、実用レベルのマテリアル設定ができます。

この記事は2026.07.06記事「BlenderアドオンBlenderChatGPTUI版の使い勝手 その2」の続きです。

1 金属マテリアル

プロンプト:スザンヌにノイズテクスチャーとカラーランプを組み合わせて、傷のある金属マテリアルを作ってください。

ChatGPTの作業方針:新規作業として、シーンを初期化してスザンヌを追加し、Noise Texture と ColorRamp を複数組み合わせた傷付き金属マテリアルをノードで作成します。ノイズを細長くマッピングして、表面の擦り傷っぽい粗さ・色ムラ・バンプを表現します。


金属マテリアル設定結果

2 レンガ柄

プロンプト:スザンヌに古びたレンガ柄のシェーダーグラフを構築してください。

ChatGPTの作業方針:新規作業として、スザンヌを作成し、手続き型のBrick Texture・Noise・ColorRamp・Bumpを組み合わせた「古びたレンガ柄」マテリアルを構築します。シーン確認用にカメラ、ライト、床も追加します。


レンガ柄マテリアル設定結果

3 金属干渉色

プロンプト:スザンヌに虹色の金属干渉色のマテリアルをつくってください。

ChatGPTの作業方針:スザンヌを新規作成し、Layer Weightを使った視線角依存の虹色ColorRampをPrincipled BSDFへ接続して、金属・低ラフネス・イリデッセンス設定の干渉色マテリアルを作成します。照明・カメラ・床も最小限セットアップします。


金属干渉色マテリアル設定結果

4 デニム布

プロンプト:スザンヌに紺色の古びてほころびのあるデニム布のマテリアルを作ってください。

ChatGPTの作業方針:新規作業として、Blender標準のスザンヌを作成し、紺色の古びたデニム布風マテリアルを割り当てます。ノイズ・波状テクスチャによる織り目と凹凸、色あせた面、裂け目風の暗い面、さらに細いカーブでほころび糸を追加して表現します。


デニム布マテリアル設定結果

5 メモ

自然言語で指示したマテリアルが複雑なノードで設定されていて、ノードをいじれば自分の好みに合わせて使えます。十分に実用レベルであると言えます。


2026年7月6日月曜日

BlenderアドオンBlenderChatGPTUI版の使い勝手 その2

 Usability of the "BlenderChatGPTUI" Blender Add-on: Part 2


I evaluated the usability of the "BlenderChatGPTUI" add-on—currently in the prototype stage—specifically from a modeling perspective.

Given how much can be accomplished using natural language, it leaves the impression of being a practical tool for real-world workflows.


試作中のBlenderアドオンBlenderChatGPTUI版の使い勝手をモデリングの側面から確かめてみました。

自然言語でこれだけできるので、実務的に使える印象を受けます。

この記事は2026.07.03記事「BlenderアドオンBlenderChatGPTUI版の使い勝手」の続きです。

1 遺物分布図の中に設置するスケール

遺物分布図の中に設置するスケールをイメージしてそのモデリングをしました。

●プロンプト

高さ2mのスケール(メモリは10㎝きざみ)とその隣に背の高さ1.7mの男性像をモデリングしてください。


結果(Blender画面)

時々遺物3D分布図や地山地形3D分布図のスケールを示すために人物像をつくるのですが、簡単なプロンプトでできるこの程度の3Dモデルで、十分に実用的です。

2 土坑のイメージモデル

土坑のポンチ絵を作ってみました。

●プロンプト

標高10mの20m×20mの平坦な土地の中央に深さ3mで、10m×7m程の楕円形に近い不整形の穴が開いている。穴の底はほぼ平らであるが、穴の底の広さは11m×8m程で、穴の開口部より広い。つまり穴の壁は全ての断面でオーバーハングしている。この地面に開いた穴をモデリングしてください。


結果(Blender画面)

予期した程度の出来ばえで、ポンチ絵作成が自由にできることを確認しました。

3 机のモデリング

机のモデリングをしてみました。

●プロンプト

Blender(4.4.3)で、机をモデリングするBlenderPythonスクリプトを作成してください。


結果(Blender画面)

机をモデリングする趣味はないのですが、AIでBlenderを操作するYouTube動画で机をモデリングしていたので、比較の意味で机を作ってみました。

自分の予想よりは質の高いモノができました。これを素材に手作業で質を高めるという意味で使えば、実用的です。

4 感想

Blender操作の中で、自然言語でこれだけできるので、実務的に使える印象を受けます。

主に遺跡における地山地形と遺物分布の3D空間関係を分析するツールとしてBlenderを使うことをイメージしています。その中で、アドオンBlenderChatGPTUI版はモデリングという側面ではかなり有用であるようだとの印象を持ちました。アドオンBlenderChatGPTUI版開発をさらに進めることにします。


2026年7月3日金曜日

BlenderアドオンBlenderChatGPTUI版の使い勝手

 Usability of the BlenderChatGPTUI Add-on


I am evaluating the usability of the BlenderChatGPTUI add-on prototype. It delivers results comparable to the web-based version of ChatGPT. Additionally, the ability to easily issue simple commands via voice input—and watch Blender react right before my eyes—makes for a very smooth and enjoyable experience.


Blenderアドオンの試作品BlenderChatGPTUI版の使い勝手を検証しています。web版ChatGPTに匹敵する成果を体験しています。また、音声入力で簡易な指示を気軽に出せて、目の前でBlenderが動くので快適です。

1 BlenderアドオンBlenderChatGPTUI版の使い勝手


BlenderアドオンBlenderChatGPTUI版の使い勝手

遺物データファイル(3D座標、42属性区分データ、55892件)をBlender3DビューポートにプロットするBlenderPythonスクリプト作成をChatGPTに指示しました。指示文は次の通りです。

「"G:/test/aaa.csv"は第1行に項目名が書いてあり、第2行以降は数値あるいは空白となっています。第1列はID、第2列~第4列はx,y,z座標、第5列以降は属性が記入されています。このファイルをBlender4.4.3の3DビューポートにID、属性を含めて点群(メッシュ)としてプロットするBlenderPythonスクリプトを作成してください。」

数秒もかからずにBlenderPythonスクリプトが生成しました。アドオンのボタンで走らせると、3Dビューポートに55892件の遺物が点群(メッシュ)でプロットされ、各点(各遺物)は多数属性情報を保持していることが確認できました。

指示文及び成果BlenderPythonスクリプトは保存して次の同様操作の参考に使えます。

2 感想

web版ChatGPTと同じように、指示文(プロンプト)を詳しく書き、あいまいさをなくすことがChatGPTのパフォーマンスを引き出すコツになります。得られる成果(BlenderPythonスクリプト)はweb版ChatGPTと遜色ありません。

3 音声入力

簡易な指示は音声入力でおこない、目の前でBlenderが動くので快適です。ある操作のショートカットを忘れた時(はじめから知らない時)などに超有効です。音声入力はWindows機能を使い、Windows+hで実現できます。

なお、このアドオン入力に限らず、パソコンで操作する全ての入力場面でこの音声入力機能は有効です。


2025年7月11日金曜日

縄文遺構実測図の縁取線(地形変換線)の高度線形補間Blenderアドオン

 Blender add-on for linear interpolation of the border lines (terrain transformation lines) of Jomon ruins survey maps


The border lines (terrain transformation lines) of Jomon ruins survey maps are not contour lines, but change in altitude. I created a Blender add-on that linearly interpolates the altitude distribution of the entire border line from several known point altitudes and displays it in the Blender3D viewport. An essential basic tool for 3D modeling of Jomon ruins has been completed.


縄文遺構実測図の縁取線(地形変換線)は等高線ではなく、高度が変化します。判明している幾つかのポイント高度から縁取線全体の高度分布を線形補間して、Blender3Dビューポートに表示するBlenderアドオンを作成しました。縄文遺構3Dモデリングの必須基礎ツールが完成しました。

1 縁取線高度線形補間Blenderアドオンの機能

このアドオンでは、3Dビューポートの真上からのビュー(テンキー7)で描いた縁取線(べジェ曲線)の頂点インデックスに2つ以上の既知の高度数値を手入力します。アドオンは、入力された既知高度数値に基づき、それ以外の頂点インデックスに線形補間(内挿、外挿)で高度数値を生成し、縁取線頂点高度が3Dビューポートで移動します。

2 縁取線高度線形補間Blenderアドオンの使い方

2-1 縁取線の作成・表示

Blender3Dビューポートに縄文遺構実測図を真上からのビュー(テンキー7)でスケール通りに配置します。この実測図を下敷きにして目的の縁取線をBezier Toolkitを使ってクリックによりべジェ曲線で生成します。

生成したべジェ曲線を編集モードで選択しておきます。

なお、閉じたべジェ曲線の場合は「オブジェクトデータプロパティ→アクティブスプライン→ループのUチェックを外す」操作で開いたべジェ曲線にしておきます。

2-2 アドオンでの高度線形補間

2-2-1 3Dビューポートにおけるインデックス(番号)の表示

アドオンの「インデックス表示切替」をクリックして、3Dビューポートの縁取線(べジェ曲線)頂点にインデックス(番号)を表示します。

2-2-2 初期化

「初期化」をクリックして、ID欄(頂点番号欄)とZ値欄(標高欄)をアドオン画面に表示します。

ID欄は全て表示されていますが、Z値欄は空欄となっています。

2-2-3 既知高度の手入力

実測図から判明している既知高度を関連する頂点のZ値欄に手入力します。補間のために、2つ以上の値を入力する必要があります。


既知高度を入力した様子(54頂点に対して既知高度を4頂点に手入力)


既知高度を入力した様子(高度は全て0となっている)

2-2-4 補間とその反映

「補間して反映」をクリックすると、Z値欄の空欄部分に線形補間された数値(高度)が生成されるとともに、3Dビューポートでべジェ曲線頂点が補間高度に移動します。


線形補間した様子(X座標、Y座標は不動)


線形補間した様子(頂点のZ座標が補間高度に移動している)


参考 土坑実測図(土坑肩線の高度が読みとれるのは4点)

2-2-5 やり直し

不都合があれば、「初期化」をクリックして、Z値欄に既知高度を入力し直し、「補間して反映」をクリックします。

2-2-6 確定

不都合がなければ、「確定」で結果を確定します。

元が閉じたべジェ曲線であった場合は、「オブジェクトデータプロパティ→アクティブスプライン→ループのUをチェックする」で開いたべジェ曲線から閉じたべジェ曲線に戻します。

3 メモ

高度線形補間を行った縁取線を使うことによって、縄文遺構3Dモデリングの精度を上げることができます。

このアドオンはBlenderバージョン4.4.3を対象にしています。

4 感想

このアドオンは「べジェ曲線の頂点インデックス(番号)表示」BlenderPythonスクリプト及び、「内挿と外挿を含む線形補間」Pythonスクリプトを最初に作成し、その2つを主な要素として新たに構成したPythonスクリプトです。それぞれの段階でのChatGPTとのやりとりはいつも以上に回数が多くなりました。恐らく、このアドオンの機能を最初からChatGPTに求めても、完成に至らなかったと想像します。

5 Pythonスクリプト


bl_info = {
    "name": "Bezier Z Interpolator",
    "blender": (4, 4, 3),
    "category": "Curve",
}

import bpy
import blf
from bpy_extras import view3d_utils
import numpy as np

handler = None

def draw_callback_px(self, context):
    obj = context.active_object
    if not obj or obj.type != 'CURVE' or obj.mode != 'EDIT':
        return
    region = context.region
    rv3d = context.space_data.region_3d
    curve = obj.data

    font_id = 0
    blf.size(font_id, 14)
    for spline in curve.splines:
        if spline.type != 'BEZIER':
            continue
        for i, bez in enumerate(spline.bezier_points):
            co_world = obj.matrix_world @ bez.co
            screen_coord = view3d_utils.location_3d_to_region_2d(region, rv3d, co_world)
            if screen_coord:
                blf.position(font_id, screen_coord.x, screen_coord.y, 0)
                blf.draw(font_id, str(i))

def register_draw_handler():
    global handler
    if handler is None:
        handler = bpy.types.SpaceView3D.draw_handler_add(
            draw_callback_px, (None, bpy.context), 'WINDOW', 'POST_PIXEL'
        )

def unregister_draw_handler():
    global handler
    if handler is not None:
        bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
        handler = None

def interpolate_z(values):
    ids = np.array([i for i, v in enumerate(values) if v != ""])
    zs = np.array([float(v) for v in values if v != ""])
    if len(ids) < 2:
        return values
    f = np.interp(range(len(values)), ids, zs)
    return [str(round(z, 4)) for z in f]

class CurveZEntry(bpy.types.PropertyGroup):
    z: bpy.props.StringProperty(name="Z", default="")

class CURVE_PT_z_interpolator_panel(bpy.types.Panel):
    bl_label = "Z値補間ツール"
    bl_idname = "CURVE_PT_z_interpolator_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Bezier Z"

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        layout.operator("curve.toggle_index_display", text="インデックス表示切替")
        layout.operator("curve.z_initialize", text="初期化")

        layout.label(text="Z値入力:")
        for i, entry in enumerate(scene.bezier_z_entries):
            row = layout.row()
            row.label(text=f"ID {i}")
            row.prop(entry, "z", text="")

        layout.operator("curve.z_interpolate", text="補間して反映")
        layout.operator("curve.z_confirm", text="確定")

class CURVE_OT_z_initialize(bpy.types.Operator):
    bl_idname = "curve.z_initialize"
    bl_label = "Zエントリ初期化"

    def execute(self, context):
        obj = context.active_object
        if not obj or obj.type != 'CURVE':
            self.report({'WARNING'}, "カーブオブジェクトを選択してください")
            return {'CANCELLED'}
        context.scene.bezier_z_entries.clear()
        for spline in obj.data.splines:
            if spline.type == 'BEZIER':
                for _ in spline.bezier_points:
                    context.scene.bezier_z_entries.add()
        self.report({'INFO'}, "Zエントリを初期化しました")
        return {'FINISHED'}

class CURVE_OT_z_interpolate(bpy.types.Operator):
    bl_idname = "curve.z_interpolate"
    bl_label = "Z値補間"

    def execute(self, context):
        entries = context.scene.bezier_z_entries
        z_list = [e.z for e in entries]
        z_interp = interpolate_z(z_list)

        obj = context.active_object
        if obj and obj.type == 'CURVE':
            index = 0
            mw = obj.matrix_world
            imw = mw.inverted()

            for spline in obj.data.splines:
                if spline.type != 'BEZIER':
                    continue

                bez_points = spline.bezier_points
                n = len(bez_points)

                for i, bez in enumerate(bez_points):
                    if index >= len(z_interp):
                        break

                    # グローバル座標でZ補正
                    world_co = mw @ bez.co
                    world_co.z = float(z_interp[index])
                    bez.co = imw @ world_co
                    entries[index].z = z_interp[index]
                    index += 1

                # 始点・終点のハンドルをたたむ
                if n >= 2:
                    p_start = spline.bezier_points[0]
                    p_end = spline.bezier_points[-1]

                    p_start.handle_left = p_start.co.copy()
                    p_start.handle_right = p_start.co.copy()
                    p_end.handle_left = p_end.co.copy()
                    p_end.handle_right = p_end.co.copy()

        return {'FINISHED'}

class CURVE_OT_z_confirm(bpy.types.Operator):
    bl_idname = "curve.z_confirm"
    bl_label = "Z値確定"

    def execute(self, context):
        self.report({'INFO'}, "Z値を確定しました")
        return {'FINISHED'}

class CURVE_OT_toggle_index_display(bpy.types.Operator):
    bl_idname = "curve.toggle_index_display"
    bl_label = "インデックス表示切替"

    def execute(self, context):
        if handler is None:
            register_draw_handler()
            self.report({'INFO'}, "インデックス表示を開始")
        else:
            unregister_draw_handler()
            self.report({'INFO'}, "インデックス表示を停止")
        return {'FINISHED'}

classes = (
    CurveZEntry,
    CURVE_PT_z_interpolator_panel,
    CURVE_OT_z_initialize,
    CURVE_OT_z_interpolate,
    CURVE_OT_z_confirm,
    CURVE_OT_toggle_index_display,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.bezier_z_entries = bpy.props.CollectionProperty(type=CurveZEntry)
    # register() では active_object にアクセスしない!

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    unregister_draw_handler()
    del bpy.types.Scene.bezier_z_entries

if __name__ == "__main__":
    register()

2025年4月27日日曜日

3D座標付き遺物リストのプロット用Blenderアドオン自作

 I made a Blender addon for plotting a list of relics with 3D coordinates


I made a Blender addon that plots a list of relics with 3D coordinates in Blender3D space. Until now I've been doing this with BlenderPython, but it's much more convenient to make it an addon. I was able to create the addon quickly with ChatGPT support.


3D座標付き遺物リストをBlender3D空間にプロットするBlenderアドオンを自作しました。これまではBlenderPythonで行ってきましたが、アドオンにした方がはるかに便利です。アドオン作成はChatGPT支援で直ぐにできました。

1 3D座標付き遺物リストのプロット用Blenderアドオン CSV Tools


3D座標付き遺物リストのプロット用Blenderアドオンの機能

csvファイルは単純に次の形式としました。

ID,X,Y,Z,TYPE

1,0,0,0,a

2,2,3,1,b

3,5,-2,4,c

csvファイルを選択できるようにしたので、便利です。

ID名(遺物名)のオブジェクトを作成し、TYPE名のコレクションに格納するので、多数遺物の扱いがしやすくなっています。

オブジェクトは0.05m×0.05m×0.05mのCUBEとして設定してあります。

アドオンづくりの楽しみという観点から言えば、色塗りを付加したり、オブジェクトの大きさや形状を変えたりといろいろ工夫はできるのですが、それらの機能を付加すると、自分の作業ではかえって煩雑になるので、それらからは試したうえで、撤退しました。

プロットしたものをgeometry nodesで凝った装飾を加える方が手軽で、自由で、効率的です。最初にプロットしたものに対して非破壊的にできます。

このアドオン作成はChatGPTの支援を受けました。

2 参考 自作アドオンのPythonスクリプト

以下のスクリプトをテキストファイルにして、拡張子をtxtからpyに変更するとPythonスクリプトになります。そのPythonスクリプトをBlenderアドオンとして読み込むと、アドオンCSV Toolsが使えるようになります。

bl_info = {
    "name": "CSVからCUBE配置ツール",
    "author": "ChatGPTアシスト",
    "version": (1, 1),
    "blender": (3, 0, 0),
    "location": "3Dビュー > サイドバー > CSV Tools",
    "description": "CSVファイルの座標にCUBEを配置し、TYPEごとにコレクションに整理するツール (ファイル選択対応)",
    "category": "Object",
}

import bpy
import csv
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator, Panel

# --- CSVからCUBEを配置するメイン処理 ---
def get_or_create_collection(name):
    if name in bpy.data.collections:
        return bpy.data.collections[name]
    else:
        new_collection = bpy.data.collections.new(name)
        bpy.context.scene.collection.children.link(new_collection)
        return new_collection

def create_cubes_from_csv(csv_file):
    with open(csv_file, newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            x = float(row['X'])
            y = float(row['Y'])
            z = float(row['Z'])
            type_name = row['TYPE']
            
            # Cube作成
            bpy.ops.mesh.primitive_cube_add(size=1, location=(x, y, z))
            obj = bpy.context.active_object
            obj.scale = (0.05, 0.05, 0.05)
            obj.name = f"Artifact_{row['ID']}"

            # 適切なコレクションに移動
            collection = get_or_create_collection(type_name)
            collection.objects.link(obj)
            bpy.context.scene.collection.objects.unlink(obj)

# --- オペレータークラス ---
class OBJECT_OT_import_csv_create_cubes(Operator, ImportHelper):
    bl_idname = "object.import_csv_create_cubes"
    bl_label = "CSVを読み込んでCUBEを配置"
    bl_options = {'REGISTER', 'UNDO'}

    filename_ext = ".csv"
    filter_glob: StringProperty(
        default="*.csv",
        options={'HIDDEN'},
    )

    def execute(self, context):
        create_cubes_from_csv(self.filepath)
        return {'FINISHED'}

# --- パネルクラス ---
class VIEW3D_PT_csv_tools(Panel):
    bl_label = "CSV Tools"
    bl_idname = "VIEW3D_PT_csv_tools"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'CSV Tools'

    def draw(self, context):
        layout = self.layout
        layout.operator(OBJECT_OT_import_csv_create_cubes.bl_idname, text="CSVファイルを選択して配置")

# --- 登録処理 ---
classes = (
    OBJECT_OT_import_csv_create_cubes,
    VIEW3D_PT_csv_tools,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()