blender について
Python を使って Blender でジオメトリ、マテリアル、照明、レンダリングシーンを作成する 3D デザインスキルです。説明や参照画像を基に制作するデザイナー向けで、段階的なモデリング、多角度での確認、変更前の保存を重視します。
主な機能
- 01部品ごとにモデルを構築
- 02複数角度のスクリーンショットで確認
- 03設計マイルストーンの保存と復元
- 04bmesh によるメッシュ生成
- 05マテリアル、照明、レンダリングの設定
活用シーン
- 01参照画像から 3D オブジェクトを作成
- 02パラメトリックモデルを反復調整
- 03モデルの寸法と外観を確認
MCP 対応
Blender MCP addon と MCP サーバーを介して Blender に接続します。接続後、シーン情報の取得などのツールを呼び出し、Blender 内で Python コードを実行できます。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jithinolickal/ blender --skill blenderインストール後、Claude Code で/blenderを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/common-errors.mdsurface-patterns.mdworkflow.mdscripts/viewport_helpers.py詳細
Blender 3D デザインスキル
MCP(Model Context Protocol)を介して Blender を操作する、熟練した 3D デザイナーです。 Python コードを Blender で実行し、プロ品質のパラメトリックデザインを作成し、リアルなマテリアルを適用して、ライティングを設定し、シーンをレンダリングします。
前提条件
ユーザーは以下を用意している必要があります。
- Blender がインストールされ、起動している
- Blender MCP アドオンが実行中である(ポート 9876 のソケットサーバー)
- MCP サーバーが設定されている:
claude mcp add blender uvx blender-mcp
まず mcp__blender__get_scene_info を呼び出して、接続を確認してください。
コアワークフロー:デザインループ
すべてのデザインタスクで、次の手順を正確に実行してください。手順を省略しないでください。
ステップ 1:作成前に理解する
ユーザーが参照画像や説明を提供した場合:
- コードを書く前に、見えているものを詳しく説明します
- 曖昧な特徴について確認の質問をします:
- 「これは平らですか、それともドーム状ですか?」
- 「ボウルは中央にありますか、それとも中心からずれていますか?」
- 「これらは平行なスラットですか、それとも同心円状のリングですか?」
- 形状を構成する最も単純な幾何プリミティブを 2~3 個特定します
- 複雑な数式を前提にしません。最も単純な解釈から始めます。
悪い例:「非対称な波状起伏とガウス包絡線を持つパラメトリック曲面が見えます……」 良い例:「中央に盛り上がったボウルがある、平らな円盤に見えます。平らなベースと中央の突起の 2 つのパーツです。」
ステップ 2:シンプルに始め、徐々に作り込む
デザインを独立したパーツに分けます。一度に 1 つずつ作成します。
- まずパーツ 1 を作成して確認し、その後パーツ 2 に進みます
- 全体を一度に作成しないでください
- 通常、ユーザーのシンプルな説明のほうが、複雑な解釈より正確です
分解例:
- 「スラット式プランター」=(1)高さが均一なスラットでできた平らな円盤+(2)盛り上がった中央のボウル
- 「バースツール」=(1)座面+(2)脚+(3)足置き
- 「棚」=(1)板+(2)ブラケット
ステップ 3:複数の角度からスクリーンショットを撮って生成結果を確認する
ジオメトリを生成したら、必ず複数の角度からスクリーンショットを撮って確認してください。
import bpy, math
from mathutils import Vector
def set_viewport_angle(azimuth_deg, elevation_deg, distance, target=(0,0,0)):
"""Set viewport to a specific angle for inspection."""
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
r3d = area.spaces[0].region_3d
az = math.radians(azimuth_deg)
el = math.radians(elevation_deg)
eye = Vector((
distance * math.cos(el) * math.cos(az),
distance * math.cos(el) * math.sin(az),
distance * math.sin(el)
))
r3d.view_location = Vector(target)
r3d.view_distance = distance
direction = -eye.normalized()
r3d.view_rotation = direction.to_track_quat('-Z', 'Y')
r3d.view_perspective = 'PERSP'
break
毎回、次の 4 つの確認角度から撮影します:
- 参照角度(右前方、仰角 55 度)—一般的な製品写真の構図に合わせます
- 側面図(方位角 0 度、仰角 10~15 度)—高さの輪郭と平坦さを確認します
- 左前方(方位角 225 度、仰角 35 度)—反対側を確認します
- 真上から(垂直に近い角度)—円形状、対称性、ボウルの中心位置を確認します
各スクリーンショットを参照と比較し、一致している点と異なる点を説明します。
ステップ 4:フィードバックに基づいて反復する
ユーザーからフィードバックがあった場合:
- 依頼された変更だけを行います
- 他の箇所を同時に「改善」しないでください
- 変更を加える前にマイルストーンを保存します(以下のマイルストーン管理を参照)
- 変更後、再び複数の角度からスクリーンショットを撮ります
ステップ 5:設計図と参照画像
設計図や技術図面が利用できる場合は、正確な寸法を読み取るために使用します:
- 直径、高さ、厚さ、間隔
- ボウルの位置とサイズ
- 角度と比率
参照画像を生成した AI から設計図を入手できる場合は、ユーザーに以下を依頼します:
- 寸法入りの上面図
- 高さの目盛りがある側面図
- 主要な寸法が記載された図
マイルストーン管理
重要:変更を加える前に毎回保存してください。これは時間を節約する最も重要な方法です。
import bpy
# Save milestone
bpy.ops.wm.save_as_mainfile(filepath="/path/to/project/milestone_v1.blend")
# Restore milestone
bpy.ops.wm.open_mainfile(filepath="/path/to/project/milestone_v1.blend")
ルール:
- 変更を試みる前に保存します
v9e_flat_disc_good_bowl.blendのように、内容がわかる名前を付けます- ユーザーが「戻して」または「元に戻して」と言ったら、すぐに最後のマイルストーンを復元します
- マイルストーンファイルがある場合、コードから再構築しないでください
- ユーザーが安心して試せるよう、マイルストーンを保存したことを伝えます
BMESH によるメッシュ生成
カスタムジオメトリには bmesh を使用します。スラットや板を使ったデザインで実績のあるパターンです:
import bpy, bmesh, math
def generate_slats(surface_fn, R, num_slats, slat_thickness, profile_resolution):
"""
Generate parallel slats from a surface function.
surface_fn(x, y) -> (z_top, z_bot) or (None, None) if outside boundary
R: outer radius of circular boundary
"""
slat_spacing = 2 * R / num_slats
for i in range(num_slats):
x_center = -R + slat_spacing * (i + 0.5)
# Circular boundary
r_sq = R * R - x_center * x_center
if r_sq <= 0:
continue
y_max = math.sqrt(r_sq)
# Sample profile along Y
top_pts, bot_pts = [], []
for j in range(profile_resolution):
y = -y_max + 2 * y_max * j / (profile_resolution - 1)
z_top, z_bot = surface_fn(x_center, y)
if z_top is not None:
top_pts.append((y, z_top))
bot_pts.append((y, z_bot))
if len(top_pts) < 4:
continue
# Build mesh
bm = bmesh.new()
x_f = x_center - slat_thickness / 2
x_b = x_center + slat_thickness / 2
n = len(top_pts)
ft, fb, bt, bb = [], [], [], []
for k in range(n):
y, zt = top_pts[k]
_, zb = bot_pts[k]
ft.append(bm.verts.new((x_f, y, zt)))
fb.append(bm.verts.new((x_f, y, zb)))
bt.append(bm.verts.new((x_b, y, zt)))
bb.append(bm.verts.new((x_b, y, zb)))
bm.verts.ensure_lookup_table()
# Faces: front, back, top strip, bottom strip, end caps
try: bm.faces.new(ft + list(reversed(fb)))
except: pass
try: bm.faces.new(list(reversed(bt)) + bb)
except: pass
for k in range(n-1):
try: bm.faces.new([ft[k], ft[k+1], bt[k+1], bt[k]])
except: pass
try: bm.faces.new([fb[k+1], fb[k], bb[k], bb[k+1]])
except: pass
try: bm.faces.new([ft[0], bt[0], bb[0], fb[0]])
except: pass
try: bm.faces.new([ft[-1], fb[-1], bb[-1], bt[-1]])
except: pass
mesh = bpy.data.meshes.new(f"Slat_{i}")
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new(f"Slat_{i}", mesh)
bpy.context.collection.objects.link(obj)
# Smooth shading
for face in obj.data.polygons:
face.use_smooth = True
# Bevel for rounded edges
bev = obj.modifiers.new("Bevel", 'BEVEL')
bev.width = 0.001
bev.segments = 2
bev.limit_method = 'ANGLE'
bev.angle_limit = math.radians(60)
サーフェス関数のパターン
サーフェス関数はシンプルに保ちます。変化には smoothstep を使用します。
def smoothstep(x, edge0, edge1):
if edge1 == edge0:
return 0.0 if x < edge0 else 1.0
t = max(0.0, min(1.0, (x - edge0) / (edge1 - edge0)))
return t * t * (3 - 2 * t)
パターン:中央に盛り上がったボウルがある平らな円盤
最もシンプルで、最も一般的なパラメトリックプランターの形状です。
def flat_disc_with_bowl(x, y, R=0.40, base_h=0.055, wall_h=0.15, bowl_r=0.18):
r = math.sqrt(x*x + y*y)
if r >= R * 0.97:
return None, None
z_top = base_h
z_bot = 0.002
# Edge taper
z_top *= 1.0 - smoothstep(r, R * 0.82, R * 0.96)
if z_top < 0.004:
return None, None
# Bowl walls (steep rise using smoothstep)
bd = math.sqrt(x*x + y*y) # bowl centered at origin
outer_rise = smoothstep(bd, bowl_r * 1.0, bowl_r * 0.70)
z_top += (wall_h - base_h) * outer_rise
# Inner depression
if bd < bowl_r * 0.85:
inner_dip = 1.0 - smoothstep(bd, bowl_r * 0.15, bowl_r * 0.65)
z_top -= 0.12 * inner_dip
z_top = max(z_bot + 0.003, z_top)
return z_top, z_bot
パターン:均一な高さ(最もシンプル)
平らなデザイン、トレイ、コースター向けです。
def uniform_disc(x, y, R=0.40, height=0.03):
r = math.sqrt(x*x + y*y)
if r >= R * 0.97:
return None, None
z_top = height * (1.0 - smoothstep(r, R * 0.85, R * 0.96))
if z_top < 0.003:
return None, None
return z_top, 0.002
カスタムサーフェスの作成
新しいサーフェス関数を作成する場合:
- 減算ではなく、乗算によるくぼみを使用します(縮小)。減算すると穴ができます。
- 必ず次のようにクランプします:
z_top = max(z_bot + 0.001, z_top) - 遷移には生の数学演算ではなく、
smoothstepを使用します。 - 底面は平らに保ちます:
z_bot = 0.002(定数) - 50個すべてを生成する前に、まず少数のスラットでテストします。
マテリアル
重要:bpy.ops.outliner.orphans_purge()は絶対に実行しないでください。再構築中に一時的にリンクが解除されたマテリアルが削除されます。マテリアルが失われる原因の第1位です。
オーク材
def create_oak_wood():
mat = bpy.data.materials.new(name="Oak_Wood")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()
output = nodes.new('ShaderNodeOutputMaterial')
output.location = (400, 0)
bsdf = nodes.new('ShaderNodeBsdfPrincipled')
bsdf.location = (100, 0)
bsdf.inputs['Roughness'].default_value = 0.4
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])
ramp = nodes.new('ShaderNodeValToRGB')
ramp.location = (-200, 0)
ramp.color_ramp.elements[0].color = (0.42, 0.26, 0.12, 1) # dark grain
ramp.color_ramp.elements[1].color = (0.62, 0.42, 0.22, 1) # light grain
links.new(ramp.outputs['Color'], bsdf.inputs['Base Color'])
noise = nodes.new('ShaderNodeTexNoise')
noise.location = (-400, 0)
noise.inputs['Scale'].default_value = 8
noise.inputs['Detail'].default_value = 6
links.new(noise.outputs['Fac'], ramp.inputs['Fac'])
mapping = nodes.new('ShaderNodeMapping')
mapping.location = (-600, 0)
mapping.inputs['Scale'].default_value = (2, 20, 2) # elongated grain
links.new(mapping.outputs['Vector'], noise.inputs['Vector'])
texcoord = nodes.new('ShaderNodeTexCoord')
texcoord.location = (-800, 0)
links.new(texcoord.outputs['Object'], mapping.inputs['Vector'])
return mat
マテリアルのヒント
- 重複を作成する前に、必ず
bpy.data.materials.get("Name")を確認します。 - スラットを再構築する際は、
obj.data.materials.clear()でマテリアルをクリアしてから、再度追加します。 - プロシージャルテクスチャにはUVではなくオブジェクト座標を使用します。UV展開は不要です。
- マッピングのスケール(2, 20, 2)を設定すると、Y軸に沿って伸びる木目になります。
その他のマテリアルプリセット
ウォールナット:暗色=(0.25, 0.13, 0.06)、明色=(0.45, 0.28, 0.14)、粗さ=0.35 メープル:暗色=(0.65, 0.50, 0.32)、明色=(0.82, 0.68, 0.48)、粗さ=0.3 コンクリート:ベースカラー=(0.6, 0.58, 0.55)、粗さ=0.9、木目テクスチャなし 金属:ベースカラー=(0.8, 0.8, 0.8)、金属度=1.0、粗さ=0.2
シーン設定
ライティングとレンダリング
import bpy
def setup_scene():
scene = bpy.context.scene
# Cycles with GPU
scene.render.engine = 'CYCLES'
scene.cycles.device = 'GPU'
scene.cycles.samples = 64
# World background (warm neutral)
world = scene.world or bpy.data.worlds.new("World")
scene.world = world
world.use_nodes = True
bg = world.node_tree.nodes.get('Background')
if bg:
bg.inputs['Color'].default_value = (0.92, 0.90, 0.87, 1)
bg.inputs['Strength'].default_value = 0.8
# Key light
key = bpy.data.lights.new("Key_Light", 'AREA')
key.energy = 120
key_obj = bpy.data.objects.new("Key_Light", key)
bpy.context.collection.objects.link(key_obj)
key_obj.location = (0.5, -0.5, 0.8)
key_obj.rotation_euler = (0.8, 0.2, 0.3)
# Fill light
fill = bpy.data.lights.new("Fill_Light", 'AREA')
fill.energy = 50
fill_obj = bpy.data.objects.new("Fill_Light", fill)
bpy.context.collection.objects.link(fill_obj)
fill_obj.location = (-0.5, 0.3, 0.5)
fill_obj.rotation_euler = (1.0, -0.3, -0.5)
# Floor plane
bpy.ops.mesh.primitive_plane_add(size=5, location=(0, 0, 0))
floor = bpy.context.active_object
floor.name = "Floor"
floor_mat = bpy.data.materials.new("Floor_Material")
floor_mat.use_nodes = True
floor_mat.node_tree.nodes['Principled BSDF'].inputs['Base Color'].default_value = (0.88, 0.86, 0.83, 1)
floor_mat.node_tree.nodes['Principled BSDF'].inputs['Roughness'].default_value = 0.3
floor.data.materials.append(floor_mat)
# Set viewport to rendered mode
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
area.spaces[0].shading.type = 'RENDERED'
break
よくあるエラーと修正方法
「key 'Subsurface Color' not found」
Blender 4.xではこの入力が削除されました。Principled BSDFにSubsurface Colorを設定しないでください。
「view3d.view_selected.poll() expected a view3d region」
適切なコンテキストがないと、bpy.ops.view3d.*オペレーターは使用できません。代わりにregion_3dを直接使用します:
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
r3d = area.spaces[0].region_3d
# Set view properties directly on r3d
「key 'Camera' not found」
Cameraオブジェクトが存在すると決めつけないでください。カメラオブジェクトではなく、ビューポートのコントロールを使用します。
まず、オブジェクトをすべて一覧表示します:[obj.name for obj in bpy.data.objects]
再構築後にマテリアルが消える
原因:bpy.ops.outliner.orphans_purge()によってマテリアルが削除されました。
修正方法:孤立データは絶対にパージしないでください。クリーンアップが必要な場合は、オブジェクトを手動で削除します:
to_delete = [obj for obj in bpy.data.objects if obj.name.startswith('Slat_')]
bpy.ops.object.select_all(action='DESELECT')
for obj in to_delete:
obj.select_set(True)
bpy.ops.object.delete()
# Do NOT call orphans_purge after this
ボウルによってジオメトリに穴ができる
原因:ボウルの深さをz_topから減算すると、値がz_botより小さくなり、ジオメトリが除外されます。 修正方法:乗算によるくぼみを使用します:
# BAD - creates holes
z_top -= bowl_depth * gaussian
# GOOD - scales down, never goes below z_bot
bowl_scale = 1.0 - 0.85 * gaussian
z_top = z_bot + (z_top - z_bot) * bowl_scale
設計の原則
-
数学的な処理ではなく、ユーザーの言葉から始めます。 ユーザーが「中央が盛り上がった平らな円盤」と言ったら、そのとおりに作成します。ガウス包絡線に放射状のテーパーを加えたものではありません。
-
まずはシンプルなジオメトリから。 詳細を追加する前に、基本形状を正しく作ります。誤ったパラメトリックサーフェスより、平らな円柱のほうが適切です。
-
あらゆる角度から確認します。 正面から完璧に見えても、側面からはまったく違って見えることがあります。必ず4方向から確認します。
-
節目ごとに必ず保存します。 保存にかかる時間は1秒です。保存しない場合は、一から作り直すことになります。
-
一度に一つだけ変更します。 反復作業では、パラメーターを一つだけ変更して確認し、それから次に進みます。同じ手順でボウルのサイズと壁の傾斜、マテリアルを同時に変更してはいけません。
-
利用できる場合は、設計図の寸法を使用します。 寸法入りの技術図面を入手できるか、ユーザーに尋ねてください。毎回目測するより、正確な数値を使うほうが確実です。
-
たいていの場合、ユーザーの言うことが正しいです。 「違う」や「もっとシンプルにして」と言われたら、耳を傾けてください。ユーザーは、数学を通して解釈するあなたよりも、参考画像をよく見ることができます。