はじめてのBlenderアドオン開発
Last Update: 2023.3.1
Blender 2.7
はじめてのBlenderアドオン開発
Blender 2.7
Last Update: 2023.3.1
3-5. blfモジュールを使ってテキストを描画する
3-4節 ではOpenGLにアクセスするAPIを利用して図形描画を行う方法を説明しましたが、同様にテキストも描画したいと思うかもしれません。 しかし、OpenGLはテキストを描画するためのAPIを用意していないことから、独自のフォント画像を用意してテキストを描画する必要があります。 このため、簡単なテキストを出力したい場合であっても、非常に手間がかかります。 しかし幸いなことに、Blenderはテキストを描画するためのAPIを提供していますので、本節で紹介します。
テキストを描画するためのAPIを使ったテキストの描画方法を理解するために、次の機能を持つアドオンを作成します。
1-5節 を参考にして以下のソースコードを入力し、ファイル名 sample_3_5.py
として保存してください。
import bpy
from bpy.props import BoolProperty, PointerProperty
import blf
bl_info = {
"name": "サンプル3-5: 3Dビューエリアにテキストを描画する",
"author": "Nutti",
"version": (2, 0),
"blender": (2, 75, 0),
"location": "3Dビュー > プロパティパネル > テキスト描画",
"description": "3Dビューエリアにテキストを描画するアドオン",
"warning": "",
"support": "TESTING",
"wiki_url": "",
"tracker_url": "",
"category": "3D View"
}
# テキスト描画
class RenderText(bpy.types.Operator):
bl_idname = "view_3d.render_text"
bl_label = "テキスト描画"
bl_description = "テキストを描画します"
__handle = None # 描画関数ハンドラ
def __handle_add(self, context):
if RenderText.__handle is None:
# 描画関数の登録
RenderText.__handle = bpy.types.SpaceView3D.draw_handler_add(
RenderText.__render, (self, context), 'WINDOW', 'POST_PIXEL')
def __handle_remove(self, context):
if RenderText.__handle is not None:
# 描画関数の登録を解除
bpy.types.SpaceView3D.draw_handler_remove(
RenderText.__handle, 'WINDOW'
)
RenderText.__handle = None
@staticmethod
def __render_text(size, x, y, s):
# フォントサイズを指定
blf.size(0, size, 72)
# 描画位置を指定
blf.position(0, x, y, 0)
# テキストを描画
blf.draw(0, s)
@staticmethod
def __get_region(context, area_type, region_type):
region = None
area = None
# 指定されたエリアを取得する
for a in context.screen.areas:
if a.type == area_type:
area = a
break
else:
return None
# 指定されたリージョンを取得する
for r in area.regions:
if r.type == region_type:
region = r
break
return region
@staticmethod
def __render(self, context):
# リージョン幅を取得するため、描画先のリージョンを得る
region = RenderText.__get_region(context, 'VIEW_3D', 'WINDOW')
# 描画先のリージョンへテキストを描画
if region is not None:
# 影の効果を設定
blf.shadow(0, 3, 0.0, 1.0, 0.0, 0.5)
# 影の位置を設定
blf.shadow_offset(0, 2, -2)
# 影の効果を有効化
blf.enable(0, blf.SHADOW)
RenderText.__render_text(
40, 40, region.height - 120, "Hello Blender world!!"
)
# 影の効果を無効化
blf.disable(0, blf.SHADOW)
RenderText.__render_text(
30, 40, region.height - 180, "Suzanne on your lap"
)
def invoke(self, context, event):
sc = context.scene
if context.area.type == 'VIEW_3D':
# 開始ボタンが押された時の処理
if sc.rt_running is False:
sc.rt_running = True
self.__handle_add(context)
print("サンプル3-5: テキストの描画を開始しました。")
# 終了ボタンが押された時の処理
else:
sc.rt_running = False
self.__handle_remove(context)
print("サンプル3-5: テキストの描画を終了しました。")
# 3Dビューの画面を更新
if context.area:
context.area.tag_redraw()
return {'FINISHED'}
else:
return {'CANCELLED'}
# UI
class OBJECT_PT_RT(bpy.types.Panel):
bl_label = "テキスト描画"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self, context):
sc = context.scene
layout = self.layout
# 開始/停止ボタンを追加
if sc.rt_running is False:
layout.operator(RenderText.bl_idname, text="開始", icon="PLAY")
else:
layout.operator(RenderText.bl_idname, text="終了", icon="PAUSE")
# プロパティの作成
def init_props():
sc = bpy.types.Scene
sc.rt_running = BoolProperty(
name="実行中",
description="実行中か?",
default=False
)
# プロパティの削除
def clear_props():
sc = bpy.types.Scene
del sc.rt_running
def register():
bpy.utils.register_module(__name__)
init_props()
print("サンプル3-5: アドオン「サンプル3-5」が有効化されました。")
def unregister():
clear_props()
bpy.utils.unregister_module(__name__)
print("サンプル3-5: アドオン「サンプル3-5」が無効化されました。")
if __name__ == "__main__":
register()
1-5節 を参考にして作成したアドオンを有効化すると、コンソールウィンドウに以下の文字列が出力されます。
サンプル3-5: アドオン「サンプル3-5」が有効化されました。
[3Dビュー] エリアのプロパティパネルを表示し、項目 [テキスト描画] が追加されていることを確認します。
有効化したアドオンの機能を使い、動作を確認します。
1 | [3Dビュー] エリアのプロパティパネルの項目 [テキスト描画] に配置されている [開始] ボタンを押します。![]() |
2 | [3Dビュー] エリアに文字列「Hello Blender world!!」「Suzanne on your lap」が表示されます。![]() |
3 | プロパティパネルの項目 [テキスト描画] に配置されている [終了] ボタンを押すとテキスト描画処理が終了し、テキストが表示されなくなります。![]() |
1-5節 を参考にして有効化したアドオンを無効化すると、コンソールウィンドウに以下の文字列が出力されます。
サンプル3-5: アドオン「サンプル3-5」が無効化されました。
本節のサンプルを見ると、描画関数の登録をはじめとして 3-4節 のサンプルのソースコードと非常に似ており、テキストを描画する処理を除き、OpenGLを使った図形描画とほぼ同じ手順でテキストを描画できることがわかります。 本節では、テキストの描画処理を中心に説明し、重複する部分については説明を省略します。 サンプルのソースコードに関して、ポイントとなる点は以下のとおりです。
テキストを描画するためのAPIは、blf
と呼ばれるモジュールに含まれています。 このため、blf
モジュールをインポートする必要があります。
import blf
複数のクラス間で共有するプロパティを次に示します。 本節のサンプルでは、共有するプロパティが1つであるため bpy.types.PropertyGroup
によるプロパティのグループ化を行っていません。
変数 | 意味 |
---|---|
rt_running |
テキスト描画中の場合は True |
3-4節 で bgl
モジュールを使って図形を描画した時と同様、テキストを描画するためには描画関数を登録する必要があります。 描画関数の登録は、__handle_add
メソッド内の bpy.types.SpaceView3D.draw_handler_add
関数で行います。 具体的な引数の型については、3-4節 と同じであるため、説明は省略します。
# 描画関数の登録
RenderText.__handle = bpy.types.SpaceView3D.draw_handler_add(
RenderText.__render, (self, context), 'WINDOW', 'POST_PIXEL')
本節のサンプルでは、描画関数が RenderText.__render
スタティックメソッド、描画するリージョンが WINDOW
であることから、第1引数に RenderText.__render
、第3引数に WINDOW
を指定します。 第2引数には、自身のインスタンスとコンテキスト情報を渡します。
描画関数である RenderText.__render
スタティックメソッドは、描画先のリージョンが更新されるたびに呼ばれます。RenderText.__render
スタティックメソッドは、描画先のリージョン情報を RenderText.__get_region
スタティックメソッドで取得した後、RenderText.__render_text
スタティックメソッドを使ってテキストを描画します。
3-4節 で説明したように、リージョンの座標値は左下が (x, y) = (0, 0) となります。 本節のサンプルでは、[ウィンドウ] リージョンの左上の座標にテキストを表示する必要があります。 しかし、リージョンの左上の座標は環境によって変化するため、単純に数値をそのまま入力して左上にテキストが表示されるように調整しただけでは、リージョンのサイズを変更したときに正しい位置にテキストを表示することができません。 このため左上の座標値を常に取得しておき、取得した座標値からの差分値を指定することで、常にリージョンの左上に表示するようにします。
[ウィンドウ] リージョンの左上の座標を取得するためには、リージョン情報を取得する必要があります。 本節のサンプルでは次の引数を受け取る RenderText.__get_region
スタティックメソッドにより、リージョン情報を取得します。
引数 | 意味 |
---|---|
context |
コンテキスト |
area_type |
取得するエリア |
region_type |
取得するリージョン |
area_type
や region_type
に指定する値は、それぞれ 2-8節 で説明したパネルクラスのクラス変数 bl_space_type
と bl_region_type
に指定したものと同じです。
RenderText.__get_region
スタティックメソッドのコードを次に示します。
@staticmethod
def __get_region(context, area_type, region_type):
region = None
area = None
# 指定されたエリアを取得する
for a in context.screen.areas:
if a.type == area_type:
area = a
break
else:
return None
# 指定されたリージョンを取得する
for r in area.regions:
if r.type == region_type:
region = r
break
return region
Blender上で開いている全てのエリア情報は、context.screen.areas
に保存されているため、RenderText.__get_region
スタティックメソッドの引数に指定したエリアのタイプ area_type
と area.type
が一致することを確認することで、目的のエリア情報を取得することができます。 本節のサンプルでは、RenderText.__get_region
スタティックメソッドの引数 area_type
に 'VIEW_3D'
が指定されているため、[3Dビュー] エリアのエリア情報を取得することができます。
取得したエリア情報から area.regions
により、エリアを構成する全てのリージョン情報を取得することができます。 エリア情報と同様に、r.type
と RenderText.__get_region
スタティックメソッドの引数 region_type
が一致すれば、目的のリージョン情報を取得することができます。 本節のサンプルでは、RenderText.__get_region
スタティックメソッドの引数 region_type
に 'WINDOW'
が指定されているため、 [ウィンドウ] リージョンのリージョン情報を取得することができます。
[ウィンドウ] リージョンのリージョン情報は、RenderText.__get_region
スタティックメソッドの戻り値 region
に保存されています。 region
からリージョンに関する情報を取得することができ、作業時間を描画する座標値を求めるために利用します。
本節のサンプルで必要となる情報は、[ウィンドウ] リージョンの左上の座標値です。 リージョンの左下の座標値が (x, y) = (0, 0) であることから、左上の座標は (x, y) = (0, リージョンの高さ)となります。 リージョン情報 region
はリージョンの高さや幅の情報を持ち、region.height
でリージョンの高さを、region.width
でリージョンの幅を取得することができます。 このため、リージョンの左上の座標は (x, y) = (0, region.height) で取得できます。 同様に、右上の座標は (x, y) = (region.width, region.height)、右下の座標は (x, y) = (region.width, 0) となります。
テキストの描画処理は、RenderText.__render_text
スタティックメソッドで行います。
@staticmethod
def __render_text(size, x, y, s):
# フォントサイズを指定
blf.size(0, size, 72)
# 描画位置を指定
blf.position(0, x, y, 0)
# テキストを描画
blf.draw(0, s)
RenderText.__render_text
スタティックメソッドに指定する引数を次に示します。
引数 | 意味 |
---|---|
size |
フォントサイズ |
x |
描画座標(X座標) |
y |
描画座標(Y座標) |
s |
描画するテキスト |
RenderText.__render_text
スタティックメソッドでは、テキストを描画するために blf
モジュールの関数を3つ使っています。 1つ目の blf.size
関数はフォントサイズを指定する関数で、次に示す引数を指定します。
引数 | 意味 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | フォントサイズ |
第3引数 | DPI |
次に blf.position
関数を使って、テキストを描画する位置を指定します。 blf.position
関数には次に示す引数を指定します。
引数 | 意味 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | 描画座標(X座標) |
第3引数 | 描画座標(Y座標) |
第4引数 | 描画座標(Z座標) |
最後に、次に示す引数を blf.draw
関数に渡して呼び出し、引数に指定された文字列を描画します。
引数 | 意味 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | 描画する文字列 |
blf.load
関数を使ってフォントを読み込み、フォントIDに blf.load
の戻り値を指定することで、読み込んだフォントを使ってテキストを描画することができます。
描画関数である RenderText.__render
スタティックメソッドは、RenderText.__get_region
スタティックメソッドで取得したリージョン情報から計算した描画座標を引数に指定して、RenderText.__render_text
スタティックメソッドを呼びます。
ここまでの処理でテキストを描画することができますが、本節のサンプルでは blf
モジュールのフォント装飾機能を使ってテキストに少し飾りつけを行っています。
# 描画先のリージョンへテキストを描画
if region is not None:
# 影の効果を設定
blf.shadow(0, 3, 0.0, 1.0, 0.0, 0.5)
# 影の位置を設定
blf.shadow_offset(0, 2, -2)
# 影の効果を有効化
blf.enable(0, blf.SHADOW)
RenderText.__render_text(
40, 40, region.height - 120, "Hello Blender world!!"
)
# 影の効果を無効化
blf.disable(0, blf.SHADOW)
RenderText.__render_text(
30, 40, region.height - 180, "Suzanne on your lap"
)
テキスト "Hello Blender world!!" は、blf
モジュールのフォント装飾機能を使って強調表示しています。 blf.shadow
関数は描画するテキストに影の効果を加えるための関数で、以下の引数を与えることで好みの影を加えることができます。 本節のサンプルでは、影の色が緑色でアルファ成分が0.5、影の大きさが3の影を加えています。
引数 | 意味 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | 影の大きさ |
第3引数 | 影の色(赤成分) |
第4引数 | 影の色(緑成分) |
第5引数 | 影の色(青成分) |
第6引数 | 影の色(アルファ値) |
blf.shadow_offset
関数を用いると、影の表示位置を変更することができます。 本節のサンプルでは、テキストの描画位置から右に2ピクセル、下に2ピクセルずらして影を表示するように設定します。
引数 | 意味 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | テキスト本体からのオフセットピクセル数(X軸) |
第3引数 | テキスト本体からのオフセットピクセル数(Y軸) |
blf.shadow_offset
関数の第2引数は画面右方向がX軸正方向、第3引数は画面上方向が正方向です。
ここまでの処理で、影の大きさや色、表示位置を設定しましたが、このままでは影が有効化されません。 影を有効化するためには、blf.enable
関数の引数に blf.SHADOW
を指定して呼び出す必要があります。 また、影を有効化した場合は描画関数終了前に無効化することを忘れないでください。 影を無効化する場合は blf.disable
関数の引数に blf.SHADOW
を指定して呼び出します。
blf.enable
関数は影の描画を有効化する時以外に、テキストの回転やテキストの一部分を切り出して表示する効果を有効化する時にも使います。 blf.enable
に指定可能な値を以下に示します。
値 | 効果 |
---|---|
ROTATION |
回転 |
CLIPPING |
切り抜き |
SHADOW |
影 |
テキストの回転の効果では、blf.rotation
関数に次の引数を指定することで、回転量を調整できます。
値 | 効果 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | 回転量(ラジアン) |
テキストの切り抜き効果では、blf.clipping
関数に以下の引数を指定することで切り抜き領域を指定できます。 なお、切り抜き領域を示す座標値には、リージョンの左下を (x, y) = (0, 0) とした座標を指定することに注意してください。
値 | 効果 |
---|---|
第1引数 | フォントID(デフォルトのフォントを使う場合は、0 を指定) |
第2引数 | 切り抜く領域のX座標の最小値 |
第3引数 | 切り抜く領域のY座標の最小値 |
第4引数 | 切り抜く領域のX座標の最大値 |
第5引数 | 切り抜く領域のY座標の最大値 |
影の有効化のところでも書きましたが、blf.enable
を有効化した効果は、描画関数を終える前に必ず無効化してください。 無効化していない状態で描画関数を終えてしまうと、blf.enable
で設定した効果が、Blender内のUI上に表示されているすべてのテキストに対して適用されてしまいます。
bgl
モジュールと blf
モジュールが提供するAPIを同時に使用することができます。 このため、描画関数を複数登録する必要はありません。
3-4節 と同様に、描画処理を停止するときに登録した描画関数の登録を解除する必要があります。
if RenderText.__handle is not None:
# 描画関数の登録を解除
bpy.types.SpaceView3D.draw_handler_remove(
RenderText.__handle, 'WINDOW'
)
RenderText.__handle = None
Blenderが提供するテキスト描画モジュール blf
を用いて、任意のテキストをBlender上に表示する方法を説明しました。 blf
モジュールを利用するためには、描画関数の登録やテキストに適用した効果の管理など面倒な部分がありますが、Blenderが提供する既存のUIに表示するよりも、描画方法に自由度があります。 また bgl
モジュールを使ってテキストを描画するよりも、比較的簡単にテキストを描画することができます。
Blenderのアドオンの中には bgl
モジュールと組み合わせることで少し変わったUIを構築しているアドオンがあるため、いろいろなアドオンを使ってソースコードを見ながら、使い方を学んでいくとよいと思います。 例えば、クリックしたマウスのボタンや押したキーボードのキーを表示するアドオン『Screencast Keys』は、bgl
モジュールや blf
モジュールを使っているため、bgl
モジュールや blf
モジュールの使い方を学ぶための取っ掛かりとしてよい参考資料になると思います。
blf
モジュールをインポートすることで利用可能になるblf
モジュールを使ってテキストを描画するためには、bgl
モジュールを用いた描画処理と同様に bpy.types.XXX.draw_handler_add
(XXX:描画するエリアにより変わる)関数を呼び出して、テキスト描画処理を行う関数、またはスタティックメソッドを登録する必要があるbpy.types.XXX.draw_handler_remove
関数を呼び出して、登録を解除する必要があるcontext.screen.areas
から取得することができるblf.enable
を呼び出すことで、描画するテキストに様々な効果を加えることができるが、必ず描画関数終了前に blf.disable
を呼び出して無効化する必要がある