2022/09/11(日) [n年前の日記]
#1 [python] PyOpenGLでテクスチャを表示
PyOpenGL を使ってテクスチャ画像を描画してみたい。
環境は、Windows10 x64 21H2 + Python 3.9.13 64bit + PyOpenGL 3.1.6 + freeglut 3.0.0 64bit。
実行結果は以下。ポリゴン1枚(GL_QUADS)にテクスチャを貼り付けることができている。
環境は、Windows10 x64 21H2 + Python 3.9.13 64bit + PyOpenGL 3.1.6 + freeglut 3.0.0 64bit。
実行結果は以下。ポリゴン1枚(GL_QUADS)にテクスチャを貼り付けることができている。
◎ ソース。 :
こんな感じのソースになった。
_01_draw_quads_tex.py
使用画像は以下。
_img_rgb.png
_img_rgba.png
_img_grayscale.png
_img_palette.png
_01_draw_quads_tex.py
from OpenGL.GL import * from OpenGL.GLUT import * from OpenGL.GLU import * import sys from PIL import Image IMGS = [ "img_rgb.png", "img_rgba.png", "img_grayscale.png", "img_palette.png", ] IMG_IDX = 0 SCRW, SCRH = 512, 512 FPS = 60 PANGLE = 60.0 window = 0 # Rotation angle for the Quads rotx = 0.0 roty = 0.0 texture = 0 def load_texture(): global texture # load image by using PIL im = Image.open(IMGS[IMG_IDX]) w, h = im.size print("Image: %d x %d, %s" % (w, h, im.mode)) if im.mode == "RGB": # RGB convert to RGBA im.putalpha(alpha=255) elif im.mode == "L" or im.mode == "P": # Grayscale, Index Color convert to RGBA im = im.convert("RGBA") raw_image = im.tobytes() ttype = GL_RGBA if im.mode == "RGB": ttype = GL_RGB elif im.mode == "RGBA": ttype = GL_RGBA glBindTexture(GL_TEXTURE_2D, glGenTextures(1)) # glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glPixelStorei(GL_UNPACK_ALIGNMENT, 4) # set texture glTexImage2D( GL_TEXTURE_2D, # target 0, # MIPMAP level ttype, # texture type (RGB, RGBA) w, # texture image width h, # texture image height 0, # border width ttype, # texture type (RGB, RGBA) GL_UNSIGNED_BYTE, # data is unsigne char raw_image, # texture data pointer ) glClearColor(0, 0, 0, 0) glShadeModel(GL_SMOOTH) # set texture repeat glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) # set texture filter glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND) def InitGL(width, height): glClearColor(0.125, 0.25, 0.5, 0.0) # background color glClearDepth(1.0) # Enables Clearing Of The Depth Buffer glDepthFunc(GL_LESS) # The Type Of Depth Test To Do glEnable(GL_DEPTH_TEST) # Enables Depth Testing glShadeModel(GL_SMOOTH) # Enables Smooth Color Shading glMatrixMode(GL_PROJECTION) glLoadIdentity() # Reset The Projection Matrix # Calculate The Aspect Ratio Of The Window gluPerspective(PANGLE, float(width) / float(height), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def ReSizeGLScene(width, height): # Prevent A Divide By Zero If The Window Is Too Small if height == 0: height = 1 # Reset The Current Viewport And Perspective Transformation glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(PANGLE, float(width) / float(height), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def DrawGLScene(): global rotx, roty glClearColor(0.125, 0.25, 0.5, 0.0) # background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Reset The View glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glEnable(GL_BLEND) glEnable(GL_TEXTURE_2D) glPushMatrix() glTranslatef(0.0, 0.0, -2.0) # translate # Rotate glRotatef(roty, 0.0, 1.0, 0.0) glRotatef(rotx, 1.0, 0.0, 0.0) glColor4f(1.0, 1.0, 1.0, 1.0) # color # draw quads x, y, z = 1.0, 1.0, 0.0 glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) # set u, v glVertex3f(-x, y, z) # Top Left glTexCoord2f(1.0, 0.0) glVertex3f(x, y, z) # Top Right glTexCoord2f(1.0, 1.0) glVertex3f(x, -y, z) # Bottom Right glTexCoord2f(0.0, 1.0) glVertex3f(-x, -y, z) # Bottom Left glEnd() glPopMatrix() glDisable(GL_TEXTURE_2D) glDisable(GL_BLEND) glutSwapBuffers() def on_timer(value): global rotx, roty rotx = -10.0 roty = roty + 1.0 glutPostRedisplay() glutTimerFunc(int(1000 / FPS), on_timer, 0) def keyPressed(key, x, y): # If escape is pressed, kill everything. ESCAPE = b"\x1b" if key == ESCAPE or key == b'q': if glutLeaveMainLoop: glutLeaveMainLoop() else: sys.exit() def main(): global window glutInit(sys.argv) glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) glutInitWindowSize(SCRW, SCRH) # glutInitWindowPosition(0, 0) window = glutCreateWindow(b"Draw texture") glutDisplayFunc(DrawGLScene) glutReshapeFunc(ReSizeGLScene) # glutFullScreen() # glutIdleFunc(DrawGLScene) glutTimerFunc(int(1000 / FPS), on_timer, 0) glutKeyboardFunc(keyPressed) InitGL(SCRW, SCRH) load_texture() glutMainLoop() if __name__ == "__main__": print("Hit ESC key to quit.") main()
使用画像は以下。
_img_rgb.png
_img_rgba.png
_img_grayscale.png
_img_palette.png
◎ 少し解説。 :
テクスチャ画像の読み込みと、OpenGL にテクスチャ画像を登録(?)するあたりは、load_texture() で処理している。
テクスチャ画像の読み込みには、PIL の Image を使った。更に、PIL (の Image) を使って、画像を必ず RGBA に変換してから画像を使う。
OpenGL に渡すためのテクスチャ画像はベタ画像データ(1ピクセル4バイトがピクセル数分ずらずらと並んでる感じのデータ)になってないといけないけれど、それについては PIL の .tobytes() で取得できる。
OpenGLにテクスチャ画像を登録するには、glTexImage2D() を利用する。
描画時にテクスチャを利用したい場合は、glEnable(GL_TEXTURE_2D) を呼んでから描画指定をする。逆に、テクスチャを利用しない場合は、glDisable(GL_TEXTURE_2D) を呼ぶ。
テクスチャ画像の読み込みには、PIL の Image を使った。更に、PIL (の Image) を使って、画像を必ず RGBA に変換してから画像を使う。
OpenGL に渡すためのテクスチャ画像はベタ画像データ(1ピクセル4バイトがピクセル数分ずらずらと並んでる感じのデータ)になってないといけないけれど、それについては PIL の .tobytes() で取得できる。
OpenGLにテクスチャ画像を登録するには、glTexImage2D() を利用する。
描画時にテクスチャを利用したい場合は、glEnable(GL_TEXTURE_2D) を呼んでから描画指定をする。逆に、テクスチャを利用しない場合は、glDisable(GL_TEXTURE_2D) を呼ぶ。
◎ 複数のテクスチャを描画。 :
前述のサンプルはテクスチャ画像を1つしか描画してないけれど、複数のテクスチャ画像を読み込んで描画できないか試してみた。
実行結果は以下。
複数のテクスチャを描画することができている。
ソースは以下。
_02_draw_quads_tex_m.py
使用画像は以下。
_img_rgb.png
_img_rgba.png
_img_grayscale.png
_img_palette.png
少し解説。
glBindTexture() を使えば、何番目のテクスチャを利用してその後の処理をするかを指定できる。
ただ…。複数のテクスチャ画像を描画した後で気づいたけれど、考えてみたら、テクスチャ画像を1枚だけ渡して、そのテクスチャ内のここからここまで切り出して利用、といった形でも済むのだよな…。特にゲーム関係はそんな感じでテクスチャを利用しているはずだし…。いやまあ、テクスチャをリピートさせて描画する時は、またちょっと違ってくるのだろうか。
実行結果は以下。
複数のテクスチャを描画することができている。
ソースは以下。
_02_draw_quads_tex_m.py
from OpenGL.GL import * from OpenGL.GLUT import * from OpenGL.GLU import * import sys from PIL import Image IMGS = [ [GL_TEXTURE0, "img_rgb.png"], [GL_TEXTURE1, "img_rgba.png"], [GL_TEXTURE2, "img_grayscale.png"], [GL_TEXTURE3, "img_palette.png"], ] SCRW, SCRH = 512, 512 FPS = 60 PANGLE = 45.0 window = 0 # Rotation angle for the Quads rotx = 0.0 roty = 0.0 textures = [] def load_texture(filename): # load image by using PIL im = Image.open(filename) w, h = im.size print("Image: %d x %d, %s" % (w, h, im.mode)) if im.mode == "RGB": # RGB convert to RGBA im.putalpha(alpha=255) elif im.mode == "L" or im.mode == "P": # Grayscale and Index Color convert to RGBA im = im.convert("RGBA") raw_image = im.tobytes() ttype = GL_RGBA if im.mode == "RGB": ttype = GL_RGB elif im.mode == "RGBA": ttype = GL_RGBA id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, id) # glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glPixelStorei(GL_UNPACK_ALIGNMENT, 4) # set texture glTexImage2D( GL_TEXTURE_2D, # target 0, # MIPMAP level ttype, # texture type (RGB, RGBA) w, # texture image width h, # texture image height 0, # border width ttype, # texture type (RGB, RGBA) GL_UNSIGNED_BYTE, # data is unsigne char raw_image, # texture data pointer ) glClearColor(0, 0, 0, 0) glShadeModel(GL_SMOOTH) # set texture repeat glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) # set texture filter glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND) return id def InitGL(width, height): glClearColor(0.15, 0.6, 0.3, 0.0) # background color glClearDepth(1.0) # Enables Clearing Of The Depth Buffer glDepthFunc(GL_LESS) # The Type Of Depth Test To Do glEnable(GL_DEPTH_TEST) # Enables Depth Testing glShadeModel(GL_SMOOTH) # Enables Smooth Color Shading glMatrixMode(GL_PROJECTION) glLoadIdentity() # Reset The Projection Matrix # Calculate The Aspect Ratio Of The Window gluPerspective(PANGLE, float(width) / float(height), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def ReSizeGLScene(width, height): # Prevent A Divide By Zero If The Window Is Too Small if height == 0: height = 1 # Reset The Current Viewport And Perspective Transformation glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(PANGLE, float(width) / float(height), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def DrawGLScene(): global rotx, roty, textures glClearColor(0.15, 0.6, 0.3, 0.0) # background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Reset The View glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glEnable(GL_BLEND) glEnable(GL_TEXTURE_2D) r = 1.05 poslist = [ [-r, r], [r, r], [-r, -r], [r, -r], ] ry = roty for i, tex_id in enumerate(textures): tx, ty = poslist[i] glPushMatrix() # change texture glBindTexture(GL_TEXTURE_2D, tex_id) # translate glTranslatef(tx, ty, -6.0) # Rotate glRotatef(ry, 0.0, 1.0, 0.0) glRotatef(rotx, 1.0, 0.0, 0.0) glColor4f(1.0, 1.0, 1.0, 1.0) # color # draw quads x, y, z = 1.0, 1.0, 0.0 glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) # set u, v glVertex3f(-x, y, z) # Top Left glTexCoord2f(1.0, 0.0) glVertex3f(x, y, z) # Top Right glTexCoord2f(1.0, 1.0) glVertex3f(x, -y, z) # Bottom Right glTexCoord2f(0.0, 1.0) glVertex3f(-x, -y, z) # Bottom Left glEnd() glPopMatrix() ry *= 1.2 glDisable(GL_TEXTURE_2D) glDisable(GL_BLEND) glutSwapBuffers() def on_timer(value): global rotx, roty rotx = 0.0 roty = roty + 1.0 glutPostRedisplay() glutTimerFunc(int(1000 / FPS), on_timer, 0) def keyPressed(key, x, y): # If escape is pressed, kill everything. ESCAPE = b"\x1b" if key == ESCAPE or key == b'q': if glutLeaveMainLoop: glutLeaveMainLoop() else: sys.exit() def main(): global window glutInit(sys.argv) glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) glutInitWindowSize(SCRW, SCRH) # glutInitWindowPosition(0, 0) window = glutCreateWindow(b"Draw textures") glutDisplayFunc(DrawGLScene) glutReshapeFunc(ReSizeGLScene) # glutFullScreen() # glutIdleFunc(DrawGLScene) glutTimerFunc(int(1000 / FPS), on_timer, 0) glutKeyboardFunc(keyPressed) InitGL(SCRW, SCRH) for _, fn in IMGS: id = load_texture(fn) textures.append(id) glEnable(GL_TEXTURE_2D) glutMainLoop() if __name__ == "__main__": print("Hit ESC key to quit.") main()
使用画像は以下。
_img_rgb.png
_img_rgba.png
_img_grayscale.png
_img_palette.png
少し解説。
glBindTexture() を使えば、何番目のテクスチャを利用してその後の処理をするかを指定できる。
ただ…。複数のテクスチャ画像を描画した後で気づいたけれど、考えてみたら、テクスチャ画像を1枚だけ渡して、そのテクスチャ内のここからここまで切り出して利用、といった形でも済むのだよな…。特にゲーム関係はそんな感じでテクスチャを利用しているはずだし…。いやまあ、テクスチャをリピートさせて描画する時は、またちょっと違ってくるのだろうか。
[ ツッコむ ]
以上です。