python + OpenGLでディスプレイなしで3Dレンダリング結果を保存

Shintaro Shiba
5 min readDec 8, 2018

--

pythonで、3Dモデルを作って光源を設定してレンダリングしたい場合がある。

有名どころのOpenGLにはPyOpenGLというpython APIを提供するライブラリがあるが、OpenGLはそもそもディスプレイなしでレンダリングができない。しかしやりたいことによってはディスプレイ表示なしで画像のmatrixを直接触ったり、画像を保存したいことがある。

他のレンダリングソフトで python API の存在するblenderはblenderの外側から叩くことができない(blenderを立ち上げることなく、python hogehoge.pyとすることができない)。

両方とも、いくつかのQiitaやstackoverflowで同様の試みに関する記事がいくつかあったが、2018年12月現在では記事が古くリンク切れを起こしていたりしてうまく動かせなかった(なのでリンクは張らない)。

動作環境

  • macOS: Mojave version 10.14.
  • OpenGL: Macプリインストール。 version 2.1 INTEL-12.2.17
  • python 3 on anaconda 4.0.0

手順

  1. PyOpenGLをインストール
pip install PyOpenGL PyOpenGL_accelerate

version 3.1.0を入れた。

2. GLFWをインストール

brew tap homebrew/versions
brew install glfw3

3. glfw (pythonライブラリ)をインストール

pip install glfw

versionは1.7.0。

動作コード例

これで準備ができた。 VISIBLEをFalseにしてwindowsを作成し、 glReadPixels でデータを取得できる。

import cv2
import numpy as np
from OpenGL.GL import *
from OpenGL.GLU import *
import glfw
light_ambient = [0.25, 0.25, 0.25]
light_position = [-10, 5, 0, 2]
def main():
DISPLAY_WIDTH = 900
DISPLAY_HEIGHT = 900
# Initialize the library
if not glfw.init():
return
# Set window hint NOT visible
glfw.window_hint(glfw.VISIBLE, False)
# Create a windowed mode window and its OpenGL context
window = glfw.create_window(DISPLAY_WIDTH, DISPLAY_HEIGHT, "hidden window", None, None)
if not window:
glfw.terminate()
return
# Make the window's context current
glfw.make_context_current(window)
gluPerspective(90, (DISPLAY_WIDTH / DISPLAY_HEIGHT), 0.01, 12)glEnable(GL_TEXTURE_2D)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient) # 環境光
glLightfv(GL_LIGHT0, GL_POSITION, light_position) # 光源の位置
glEnable(GL_LIGHT0)
glEnable(GL_LIGHTING)
glRotatef(-90, 1, 0, 0) # Straight rotation
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glRotatef(285, 0, 0, 1) # Rotate yaw
glTranslatef(-5, -3, -2) # Move to position
# Draw rectangle
glBegin(GL_QUADS)
glColor3f(1, 0, 0)
glVertex3f(2, 2, 0)
glVertex3f(2, 2, 2)
glVertex3f(2, 6, 2)
glVertex3f(2, 6, 0)
glEnd()
image_buffer = glReadPixels(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, OpenGL.GL.GL_RGB, OpenGL.GL.GL_UNSIGNED_BYTE)
image = np.frombuffer(image_buffer, dtype=np.uint8).reshape(DISPLAY_WIDTH, DISPLAY_HEIGHT, 3)
cv2.imwrite("image.png", image)glfw.destroy_window(window)
glfw.terminate()
if __name__ == "__main__":
main()

ここを参考にした。

将来記事が古くなる可能性があるので、できなかった場合ご容赦ください。

--

--