アストラルプリズム

PC、スマホ、ゲームなどの備忘録と日記

blender python 最速伝説

blender bpyでops系が遅いのはわかってるがdata系ではmesh、bmesh、Matrix・・・どれが早いのか実際に調べてみた

簡単に立方体をグローバルY方向に3移動を100回繰り返す
長いので最初に結論。

・オブジェクトモードならobj.locationとmatrix_world.translationを使うと良い
・原点の位置を変えずに全体の形を変形させたい場合はMatrixローカル座標)が早い
・編集モードのみの頂点移動はmeshよりbmeshが早い
・オブジェクトが沢山ある場合はmeshかオブジェクトモードでのbmeshを使うと良い
・編集モードとオブジェクトモードを行き来するのも結構時間がかかる



ops.transform.translate→0.01秒
obj.location→0.0001
matrix_world.translation→0.0002秒
mesh→0.001秒
bmesh(編集モード)→0.0004秒
bmesh(編集モードとオブジェクトモードを行き来した)→0.04秒
bmesh(オブジェクトモード)→0.004秒
編集モードとオブジェクトモードを行き来しただけ→0.04秒


以下詳細

ops

import bpy ,time
start = time.time()

for i in range(0,100):    
    bpy.ops.transform.translate(value=(0, 3, 0), orient_type='GLOBAL')

end =time.time()
elapsed_time = end - start
print(elapsed_time)

結果 0.010007858276367188
0.01秒

data系(obj.location)

import bpy ,time
start = time.time()
obj = bpy.context.object
for i in range(0,100):    
    obj.location.y = (i+1)*3
end =time.time()
elapsed_time = end - start
print(elapsed_time)

結果 0.0
測定されないので100回から1万回に変更したらやっと測定された。
0.010066747665405273/100で
0.0001秒
つまりopsの100倍速い。

data系(Matrix)

import bpy ,time

start = time.time()

from mathutils import Matrix

obj = bpy.context.object
for i in range(0,100):    
   obj.matrix_world.translation.y=(i+1)*3

end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果 0.0
公平に最初にmathutilsをインポートする時間も測定。
測定されないので100回から1千回に変更したら測定された。
0.001949310302734375/10で
0.0002秒
opsの52倍

locationより2倍遅いがインポートの時間も微小と思われるので良さそう

data系(Matrixローカル座標)

import bpy,time
start = time.time()
from mathutils import Matrix,Vector
context = bpy.context
obj = context.object
msh = obj.data

for i in range(0,100):   
    inv = obj.matrix_world.inverted()
    Tr = Matrix.Translation(inv@Vector((0,3,0)))
    msh.transform(Tr)

end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果 
千回で測定
0.0020112991333007812/10
0.0002秒
Matrix.Translationは原点は動かないのでmeshやbmeshと同じ。
原点を移動せずに全体の形を変えたい時はこれが早い

data系(mesh)

ローカル座標のみの移動

import bpy ,time

start = time.time()
obj = bpy.context.object
msh = obj.data
for i in range(0,100):    
   for v in msh.vertices:
       v.co.y = v.co.y + 3
      
end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果
千回にしたら測定された
0.010063648223876953/10
0.001秒


グローバル座標に変換しつつ移動。

import bpy ,time

start = time.time()

from mathutils import Matrix,Vector
obj = bpy.context.object
inv = obj.matrix_world.inverted()
msh = obj.data
for i in range(0,100):    
   for v in msh.vertices:
       v.co = v.co + inv @ Vector((0,3,0))
      
end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果 
千回にして測定された
0.010052680969238281/10
0.001秒
matrix_worldを使用しているのでその分遅くはなるがもともとが0.001秒、matorixが0.00019秒なので微小。

data系(bmesh)

import bpy ,time

start = time.time()
import bmesh
bpy.ops.object.mode_set(mode='EDIT')

obj = bpy.context.object
msh = obj.data
bm=bmesh.from_edit_mesh(msh)
for i in range(0,100):    
   for v in bm.verts:
       v.co.y = v.co.y + 3

bpy.ops.object.mode_set(mode='OBJECT')      
end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果
一万回で以下略。
0.035222530364990234/100
0.0004秒

meshよりbmeshのほうが早くて驚いた。
しかし、bmeshはオブジェクトモードと編集モードを行き来する必要があるのでそこも割と時間がかかると思われる。

import bpy ,time

start = time.time()

for i in range(0,100):    
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.object.mode_set(mode='OBJECT') 
     
end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果
0.04009270668029785
0.04秒なのでまとめて作業する必要ある。

編集モードとオブジェクトモードを行き来しつつbmeshで移動

import bpy ,time

start = time.time()
import bmesh
obj = bpy.context.object
msh = obj.data
for i in range(0,100):    
    bpy.ops.object.mode_set(mode='EDIT')    
    bm = bmesh.from_edit_mesh(msh)
    for v in bm.verts:
        v.co.y = v.co.y + 3
    bpy.ops.object.mode_set(mode='OBJECT') 
     
end = time.time()
elapsed_time = end - start
print(elapsed_time)

結果 0.04002857208251953
0.04秒
まあ、こんな感じかな・・・。

同じくbmeshで編集モードに移動しなくても出来る方法でやってみた。
(たくさんのオブジェクトを行き来する想定)

import bpy ,time

start = time.time()
import bmesh

for i in range(0,100):
    obj = bpy.context.object
    msh=obj.data

    bm = bmesh.new()
    bm.from_mesh(obj.data)
    for v in bm.verts:          
            v.co.y = v.co.y + 3
    bm.to_mesh(msh)
    bm.free()

end = time.time()
elapsed_time = end - start
print(elapsed_time)
bpy.context.view_layer.update()

結果
0.003983259201049805
0.004秒
沢山のオブジェクトを行き来する場合ならこちらが圧倒的に早い。