美味しい焼き鳥屋さん

初心者による技術系のメモ

【Blender】カメラフォーカスの座標を取得する Python


とりあえずカメラのフォーカス座標の取得ができたのでメモ

カメラのフォーカス座標取得までのフロー

  1. 位置(Location)、回転(Rotation)、カメラからの距離(Distance)の取得
  2. 回転(Rotation)、カメラからの距離(Distance)を用いて極座標から直角座標へ変換(極座標変換)

詳細

1.情報の取得方法

位置

bpy.context.active_object.location

回転

bpy.context.active_object.rotation_euler

※取得する値はクォータニオンではなくオイラー
※UI上表示されているのは度数法だが、BlenderAPI上で取得できるのは弧度法なので注意

カメラからの距離

bpy.context.active_object.data.dof.focus_distance

(実際に公開したアドオンの場合はリストに登録したデータからオブジェクトを参照している)
細かい話になるが、位置や回転については後ほど各要素(x、y、z)を分解して処理を行うので
取得する段階で別々の変数に値を代入した。

位置(x軸の値を取得するとき)

bpy.context.active_object.location.x

回転(x軸の値を取得するとき)

bpy.context.active_object.rotation_euler.x

2.極座標変換について

moromi-senpy.hatenablog.com
Python極座標変換を行うにあたって先の記事に非常に助けられました。

極座標変換を行うにあたって必要な情報の取得

極座標変換をするにあたって必要な「仰角」「方位角」「動径」を取得する
f:id:t0rry:20210919123444p:plain
方位角:Θ 仰角:φ 動径:r

仰角の取得(φ)

f:id:t0rry:20210919124330p:plain
X軸方向の回転が仰角の元データになる。

bpy.context.active_object.rotation_euler.x

ここで注意したいのが、X軸の回転がそのまま仰角にならないこと
X軸回転が0の場合の極座標Θは180度(π)になります。
f:id:t0rry:20210919125405p:plain
よって、単純にX軸回転の値を扱うのではなく仰角向けに計算を挟む必要があります。

方位角(Θ)

f:id:t0rry:20210919130233p:plain
Z軸方向の回転が方位角の元データになる。

bpy.context.active_object.rotation_euler.z

仰角と同様に方位角についてもそのまま値を利用してΘにならない
Z軸回転が0の場合の極座標φは90度(2/1π)になります。
こちらも方位角向けの計算が必要です。

動径(r)

f:id:t0rry:20210919130951p:plain
カメラの被写界深度の撮影距離が動径になる

bpy.context.active_object.data.dof.focus_distance
極座標変換

取得したΘ、φ、rをもとに極座標変換をする
x = r sinΘ cosφ
y = r sinφ sinφ
z = r cosΘ

この計算を行うにあたってNumpyという数値演算ライブラリを使いました。

import numpy as np

numpyをimportするのを忘れずに

        newPosition = np.empty([1,3], dtype=np.float64)
        newPosition[:,0] = bpy.context.active_object.data.dof.focus_distance * np.sin(theta) * np.cos(phi)
        newPosition[:,1] = bpy.context.active_object.data.dof.focus_distance * np.sin(theta) * np.sin(phi)
        newPosition[:,2] = bpy.context.active_object.data.dof.focus_distance * np.cos(theta)

先ほど冒頭に載せたサイトのコードを参考にさせていただいています。

カメラの位置を反映させる

極座標変換で演算したnewPositionのx,y,zについてはカメラの位置を反映させていない、「原点からのフォーカス座標」。
極座標変換で導き出した「原点からのフォーカス座標」+カメラそのものの「カメラの位置」を加算することで実際のカメラの「フォーカス座標」が取得できる

実際にアドオンで使用したコード

class CML_OT_ViewCoordinate(bpy.types.Operator):
    """Calculation to View Coordinate """
    bl_idname = "camera_list.view_coordinate"
    bl_label = "view coordinate"

    @classmethod
    def poll(cls, context):
        return context.scene.camera_list

    def execute(self,context):
        #00 camera_list,index
        index = context.scene.list_index
        camera_list =context.scene.camera_list[index]

        #01 get camera's locations(x,y,z)
        original_location = camera_list.ob.location 
        

        #02 get camera's rotations
        #02-1 (euler)
        original_angle_x  = camera_list.ob.rotation_euler.x
        original_angle_z  = camera_list.ob.rotation_euler.z

        
        #03 calculate theta (180° = π = np.pi )

        if original_angle_x < np.pi:
            theta = np.pi - original_angle_x


        else:
            theta =  3 * np.pi -original_angle_x
            print("B :x< 2pi , x > pi")
            


 
        #04 calculate phi
        if original_angle_z < np.pi:
            phi = original_angle_z + (np.pi / 2 )


        else:
            phi = original_angle_z + (np.pi / 2 )
            print("B :z > 2pi , z > pi")


        #05 get camera's focus distance
        original_view_distance = camera_list.ob.data.dof.focus_distance
        
        
        newPosition = np.empty([1,3], dtype=np.float64)
        newPosition[:,0] = original_view_distance * np.sin(theta) * np.cos(phi)
        newPosition[:,1] = original_view_distance * np.sin(theta) * np.sin(phi)
        newPosition[:,2] = original_view_distance * np.cos(theta)
        
        x = newPosition[:,0]
        y = newPosition[:,1]
        z = newPosition[:,2]
        

        


        
        
        return{'FINISHED'}

※camera_listについてはカスタムプロパティでカメラの情報が代入されている

※プログラミング初心者が故に誤りがあると思いますが、参考になればと思います。