OpenGL を Python3 から利用するための環境設定および簡単なサンプルコード (Linux)
[最終更新] (2019/09/02 23:03:21)
最近の投稿
注目の記事

概要

コンピュータグラフィックスのレンダリングライブラリの一つである OpenGL を Python3 から利用するための Linux 環境を準備して、設定例およびサンプルコードを記載します。特にここでは Debian9 を利用します。

ModernGL インストール

Python バインディングのうち、ここでは ModernGL を介して OpenGL を利用することにします。ただし、必要となる Python は 3 系です。

Python3 および pip、ipythonnumpy、matplotlib、pil

sudo apt install python3
sudo apt install python3-pip
sudo apt install python3-ipython
sudo apt install python3-numpy
sudo apt install python3-matplotlib
sudo apt install python3-pil

ModernGL

以下のいずれかでインストールできます。

/usr/bin/python3 -m pip install ModernGL
/usr/bin/pip3 install ModernGL

動作確認

IPython 起動

/usr/bin/python3 -m IPython

以下の内容を実行します。

import moderngl
ctx = moderngl.create_standalone_context()
buf = ctx.buffer(b'Hello World!')  # allocated on the GPU
buf.read()

出力例

b'Hello World!'

3D レンダリング Hello World

3D モデルから 2D 画像を生成する 3D レンダリングの簡単な例を記載します。IPython 等で以下の内容を実行します。

DISPLAY=:0 /usr/bin/python3 -m IPython

OpenGL ではまずコンテキストが必要になります

import moderngl
ctx = moderngl.create_standalone_context()

下記レンダリングパイプラインにおいて、頂点シェーダーへの入力となる、3 つの頂点の情報を準備します。

import numpy as np
vertices = np.array([
     0.5,  0.5, 0.0,
    -0.5,  0.5, 0.0,
    -0.5, -0.5, 0.0
])

これらをバイトに変換して GPU 上にデータを格納し、Buffer (Vertex Buffer Object) を作成します。

vbo = ctx.buffer(vertices.astype('float32').tobytes())

ModernGL をインストールすると付属のバージョンの OpenGL がインストールされます。OpenGL には OpenGL Shading Language (GLSL) とよばれる、シェーダーを記述するための言語の処理系が実装されています。GLSL を用いて、以下の二つのシェーダーを作成します。これらは一連のレンダリングパイプラインで利用されます。

  • Vertex Shader (頂点シェーダー)
    • 入力された、3D モデルにおけるローカル座標系の頂点情報をワールド座標系に変換して、更にあるカメラの視点から見たときの座標系に変換して 2 次元情報に射影します。
  • Fragment Shader (ピクセルシェーダー)
    • 前工程のレンダリングパイプラインで生成されたフラグメントに色情報等を設定します。

GLSL version 330 を利用しています。簡単のため、入力された三次元頂点情報 vec3 をそのまま加工せずに返しています。その際、同次座標系で返す必要があるため 1.0 を追加して vec4 にしています。また、各フラグメントの色も簡単のため RGBA で青色を返しています。

prog = ctx.program(
    vertex_shader='''
        #version 330
        in vec3 in_vert;
        void main() {
            gl_Position = vec4(in_vert, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330
        out vec4 f_color;
        void main() {
            f_color = vec4(0.0, 0.0, 1.0, 1.0);
        }
    '''
)

頂点情報 Buffer をシェーダー prog に変数名を指定して渡します。結果として Vertex Array Object (vao) が得られます。

vao = ctx.simple_vertex_array(prog, vbo, 'in_vert')

レンダリング先となる 2D フレーム Frame Buffer Object を用意します。500x500 ピクセルとしてみます。

fbo = ctx.simple_framebuffer((500, 500))
fbo.use()
fbo.clear(0.0, 0.0, 0.0, 1.0)

レンダリングを実行します。結果が fbo に格納されます。

vao.render()

結果は RAW 画像であるため、MatplotlibOpenCV からは直接読み込むことが困難です。pillow(PIL) の Image.frombytes を利用して NumPy の ndarray に変換することができます。

from PIL import Image
myimg = Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1)

以下のようにして表示できます。

from matplotlib import pyplot as plt
plt.imshow(myimg)
plt.show()

Uploaded Image

頂点毎に異なる色を設定

先程の Hello world プログラムでは各頂点をすべて青色にしました。色を変えるために頂点シェーダーへの引数を増やしてみます。

import moderngl
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt

# OpenGL コンテキスト
ctx = moderngl.create_standalone_context()

# 頂点情報 (x,y,z,r,g,b)
vertices = np.array([
     0.5,  0.5, 0.0, 1.0, 0.0, 0.0,
    -0.5,  0.5, 0.0, 0.0, 1.0, 0.0,
    -0.5, -0.5, 0.0, 0.0, 0.0, 1.0
])
vbo = ctx.buffer(vertices.astype('float32').tobytes())

# シェーダープログラム
prog = ctx.program(
    vertex_shader='''
        #version 330
        in vec3 in_vert;
        in vec3 in_color;
        out vec3 v_color;

        void main() {
            v_color = in_color;
            gl_Position = vec4(in_vert, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330
        in vec3 v_color;
        out vec4 f_color;
        void main() {
            f_color = vec4(v_color, 1.0);
        }
    '''
)

# 色情報の引数を増やしています
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_color')

# 2D フレームの作成
fbo = ctx.simple_framebuffer((500, 500))
fbo.use()
fbo.clear(0.0, 0.0, 0.0, 1.0)

# レンダリングの実行
vao.render()

# RAW 画像から ndarray に変換して描画
myimg = Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1)
plt.imshow(myimg)
plt.show()

Uploaded Image

二次元 XY 平面内での回転

GPU 内で利用する uniform 定数を宣言しています。

import moderngl
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt

# OpenGL コンテキスト
ctx = moderngl.create_standalone_context()

# 頂点情報 (x,y,z,r,g,b)
vertices = np.array([
     0.5,  0.5, 0.0, 1.0, 0.0, 0.0,
    -0.5,  0.5, 0.0, 0.0, 1.0, 0.0,
    -0.5, -0.5, 0.0, 0.0, 0.0, 1.0
])
vbo = ctx.buffer(vertices.astype('float32').tobytes())

# シェーダープログラム
prog = ctx.program(
    vertex_shader='''
        #version 330
        in vec3 in_vert;
        in vec3 in_color;
        out vec3 v_color;

        // uniform によって GPU 内で利用する定数を宣言できます。
        // 引数ではなく属性値として CPU から値を設定します。
        uniform vec3 scale;
        uniform float rotation;

        void main() {
            // 二次元の回転行列
            // mat3 の仕様上、行と列を転置したものを設定します。
            mat3 rot = mat3(
                cos(rotation), sin(rotation), 0.0,
                -sin(rotation), cos(rotation), 0.0,
                0.0, 0.0, 1.0
            );

            v_color = in_color;
            gl_Position = vec4((rot * in_vert) * scale, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330
        in vec3 v_color;
        out vec4 f_color;
        void main() {
            f_color = vec4(v_color, 1.0);
        }
    '''
)

# prog 内で宣言した GPU 用の定数の値を設定します。
prog['scale'].value = (2.0, 2.0, 1.0)
prog['rotation'].value = np.deg2rad(90)

# 頂点情報 (位置、色) をシェーダーに渡します。
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_color')

# 2D フレームの作成
fbo = ctx.simple_framebuffer((500, 500))
fbo.use()
fbo.clear(0.0, 0.0, 0.0, 1.0)

# レンダリングの実行
vao.render()

# RAW 画像から ndarray に変換して描画
myimg = Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1)
plt.imshow(myimg)
plt.show()

反時計回りに 90 度回転させてから 2 倍に拡大しています。mat3 は column-major order で値を指定する必要があることに注意します。

Uploaded Image

オブジェクトファイルの利用

3D オブジェクトはメッシュによって表現できます。メッシュは頂点やフェイス等から成ります。通常フェイスには三角形を利用します。メッシュの情報を格納するファイルフォーマットとしては OBJ がよく用いられます。以下は簡単な例です。

sample.obj

v   0.5  0.5  0.0
v  -0.5  0.5  0.0
v  -0.5 -0.5  0.0
v   0.5 -0.5  0.0

f 1 2 3
f 3 4 1

v は頂点のローカル座標です。f では三角形フェイスで利用する頂点 v のインデックスを三つ指定します。今回のメッシュでは三角形フェイスが二つで正方形を表現しています。OBJ ファイルを読み込むために何らかのモジュールをインストールすると便利です。

/usr/bin/python3 -m pip install objloader

以下のようなレンダリング結果が得られます。

Uploaded Image

import moderngl
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from objloader import Obj

# OpenGL コンテキスト
ctx = moderngl.create_standalone_context()

# 頂点情報 (x,y,z)
obj = Obj.open('./sample.obj')
vbo = ctx.buffer(obj.pack('vx vy vz'))

# シェーダープログラム
prog = ctx.program(
    vertex_shader='''
        #version 330
        in vec3 in_vert;
        void main() {
            gl_Position = vec4(in_vert, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330
        out vec4 f_color;
        void main() {
            f_color = vec4(0.0, 0.0, 1.0, 1.0);
        }
    '''
)

# Vertex Array Object の作成
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert')

# 2D フレームの作成
fbo = ctx.simple_framebuffer((500, 500))
fbo.use()
fbo.clear(0.0, 0.0, 0.0, 1.0)

# レンダリングの実行
vao.render()

# RAW 画像から ndarray に変換して描画
myimg = Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1)
plt.imshow(myimg)
plt.show()

オブジェクトへのテクスチャの貼り付け

sample.obj

v   0.5  0.5  0.0
v  -0.5  0.5  0.0
v  -0.5 -0.5  0.0
v   0.5 -0.5  0.0

vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0

f 1/3 2/4 3/1
f 3/1 4/2 1/3

vt はテクスチャ画像における座標です。フェイスでは / 区切りでどのテクスチャ座標を頂点座標と対応させるか指定します。

import moderngl
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from objloader import Obj

# OpenGL コンテキスト
ctx = moderngl.create_standalone_context()

# 頂点座標 (x,y,z)、テクスチャ座標 (x,y)
obj = Obj.open('./sample.obj')
vbo = ctx.buffer(obj.pack('vx vy vz tx ty'))

# シェーダープログラム
prog = ctx.program(
    vertex_shader='''
        #version 330
        in vec3 in_vert;
        in vec2 in_text;
        out vec2 v_text;
        void main() {
            v_text = in_text;
            gl_Position = vec4(in_vert, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330
        uniform sampler2D Texture;
        in vec2 v_text;
        out vec4 f_color;
        void main() {
            f_color = vec4(texture(Texture, v_text).rgb, 1.0);
        }
    ''',
)

# Vertex Array Object の作成
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_text')

# テクスチャ画像の読み込み
textureimg = Image.open('./myimage.png').transpose(Image.FLIP_TOP_BOTTOM).convert('RGB')
texture = ctx.texture(textureimg.size, 3, textureimg.tobytes())
texture.build_mipmaps()
texture.use()

# 2D フレームの作成
fbo = ctx.simple_framebuffer((500, 500))
fbo.use()
fbo.clear(0.0, 0.0, 0.0, 1.0)

# レンダリングの実行
vao.render()

# RAW 画像から ndarray に変換して描画
myimg = Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1)
plt.imshow(myimg)
plt.show()

Uploaded Image

オブジェクトの回転

この続きが気になる方は

OpenGL を Python3 から利用するための環境設定および簡単なサンプルコード (Linux)

残り文字数は全体の約 17 %
tybot
100 円
関連ページ
    概要 FFI (Foreign Function Interface) の一つである ctypes を利用すると、C 言語のライブラリを Python から利用できます。サンプルコードを記載します。 適宜参照するための公式ドキュメント libm の sqrt を利用する例 main.py #!/usr/bin/python # -*- coding: utf-8 -*- from c
    概要 立方体を二つ配置して回転させてみます。ライブラリを用いずに OpenGL API を直接利用します。描画部分のみを IPython で検証するためのソースコードはこちらです。 wget https://gist.githubusercontent.com/harubot/df886254396a449038ee542ed317f7b3/raw/92216e02d0210b9d8177056
    概要 OpenGL のフレームバッファには複数のカラーバッファを割り当てることができます。フレームバッファはカラーバッファの他にデプスバッファとステンシルバッファを持ちます。これらバッファはすべてレンダーバッファとよばれるメモリ領域です。フラグメントシェーダで out として得られる出力はカラーバッファに格納されます。一つのフラグメントシェーダに
    概要 コンピュータグラフィックスのレンダリングライブラリの一つ OpenGL はプラットフォームに依存しない仕様となっています。プラットフォームの一つに X11 があります。プラットフォームに依存する仕様は EGL (Embedded-System Graphics Library) にまとめられています。EGL は OpenGL とネイティブプラットフォームの間のインタフェースとして機能します
    概要 ROS による開発で必要となる 3D モデルは STL や COLLADA データとして作成できます。FreeCAD と Blender を用いて直方体のデータを作成してみます。いずれの場合も、ROS 等で扱うために単位はメートルで出力します。 FreeCAD による直方体 STL の作成 STL はフェイスに三角形を利用した 3D モデル
    概要 ロボットアプリケーションの開発環境の一つ OpenRAVE (Open Robotics Automation Virtual Environment) の環境を構築するための手順を記載します。ここでは特に Debian9 を利用します。 インストール 依存パッケージ ビルドツール関連 sudo apt install git build-essential cmake
    概要 Qt 等で開発された X11 アプリケーションを利用するためには X サーバのクライアントが必要になります。X サーバは仮想的に Xvfb コマンドで作成することができます。X サーバのクライアントには fluxbox などの X11 ウィンドウマネージャ、ssh -X や x11vnc、その他 Qt 等で開発された X11 アプリケーションがあります。
    概要 3D アプリケーション間でデータを交換するためのファイルフォーマットの一つに COLLADA (COLLAborative Design Activity) があります。コンピュータグラフィックスのレンダリングに必要な情報およびその他付随する情報を格納できます。COLLADA の仕様にしたがった XML スキーマファイルの拡張子は通常
    概要 ポイントクラウド (点群) は空間内の点 (x,y,z) の集合です。例えば LiDAR (Light Detection and Ranging、レーザースキャナ、3D スキャナ) を利用して取得できます。メッシュで物体を表現している場合と区別します。 画像には通常の RGB カラー画像だけでなく、距離情報を濃淡として保存した距離画像
    概要 Open Dynamics Engine (ODE) を用いた動力学シミュレーションを行います。ここでは ode-python を利用して Python から実行します。 インストール 以下は Linux 環境の例です。 ODE ビルド ODE に付属する DrawStuff をビルドするためには OpenGL と GLUT