銀月の符号

Python 使い見習いの日記・雑記

PyOpenGL + PIL でテクスチャ貼り

www.komoto.orgOpenGL入門 sample09PyOpenGLPIL で再現。動作させるにはこの2つのライブラリと texture2.ppm が必要。

テクスチャを作成するには PIL で画像を読み込んで、 tostring() でバイト列に直してから glTexImage2D にわたせば OK 。つまり PIL が読める画像形式ならばテクスチャとしてつかえる。オリジナルでは C言語PPM画像をがんばって読んでいるのに対して、 PIL だと Image.open するだけ。やはり PIL は便利すぎる。

# coding: utf-8
u"""
参考
OpenGL Programing
サンプル9 ミップマップ
http://www.komoto.org/opengl/sample09.html

これを PyOpenGL + PIL に移植してみた

"""

import sys
import os
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image

tex = 0 # テクスチャ番号
mipmap = 0 # ミップマップ番号

def check_size(img):
    u"""
    テクスチャ用画像のサイズが 2 の累乗になっているか調べる
    """
    for size in img.size: # img.size は幅・高さのタプル
        while True:
            if (size & 1) != 0:
                break
            size >>= 1
        if size != 1:
            return False
    return True

def ppm2texture(ppm_path):
    u"""
    PPM ファイルを読み込んでテクスチャを返す
    """
    ppm = Image.open(ppm_path)
    assert check_size(ppm)
    w, h = ppm.size # 幅・高さのタプル
    data = ppm.tostring() # 画像データを文字列化

    # テクスチャ番号を取得
    tex = glGenTextures(1)
    # 取得した番号のテクスチャを使うように設定
    glBindTexture(GL_TEXTURE_2D, tex)
    # テクスチャを生成する
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h,
            0, GL_RGB, GL_UNSIGNED_BYTE, data)
    return tex

def ppm2mipmap(ppm_path):
    u"""
    PPM ファイルを読み込んでミップマップを返す
    """
    ppm = Image.open(ppm_path)
    assert check_size(ppm)
    w, h = ppm.size
    data = ppm.tostring()

    tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, tex)
    # ミップマップを生成する
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, w, h,
                      GL_RGB, GL_UNSIGNED_BYTE, data)
    return tex

def display_func():
    u"""
    描画用関数
    """
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glColor3f(1.0, 1.0, 1.0)

    # テクスチャマップを有効にする
    glEnable(GL_TEXTURE_2D)
    # 1 つ目のテクスチャを設定
    glBindTexture(GL_TEXTURE_2D, tex)
    # テクスチャマップの方法を設定
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

    glBegin(GL_QUADS) # 四角形を描く
    glTexCoord2f(0.0, 1.0) # テクスチャ画像での位置を指定
    glVertex3f(2.0, -1.0, -50.0) # 頂点をセット

    glTexCoord2f(5.0, 1.0)
    glVertex3f(2.0, -1.0, 0.0)

    glTexCoord2f(5.0, 0.0)
    glVertex3f(2.0, 1.0, 0.0)

    glTexCoord2f(0.0, 0.0)
    glVertex3f(2.0, 1.0, -50.0)
    glEnd()

    # 2 つ目のテクスチャを設定
    glBindTexture(GL_TEXTURE_2D, mipmap)
    # テクスチャマップの方法を設定
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                    GL_LINEAR_MIPMAP_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

    glBegin(GL_QUADS) # 四角形を描く
    glTexCoord2f(5.0, 1.0) # テクスチャ画像での位置を指定
    glVertex3f(-2.0, -1.0, -50.0) # 頂点をセット

    glTexCoord2f(0.0, 1.0)
    glVertex3f(-2.0, -1.0, 0.0)

    glTexCoord2f(0.0, 0.0)
    glVertex3f(-2.0, 1.0, 0.0)

    glTexCoord2f(5.0, 0.0)
    glVertex3f(-2.0, 1.0, -50.0)
    glEnd()

    glFlush()

def reshape_func(width, height):
    u"""
    ウィンドウサイズ更新時の処理
    """
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glFrustum(-1.0, 1.0, -1.0, 1.0, 3.0, 10000.0)
    glMatrixMode(GL_MODELVIEW)

def main():
    global tex # テクスチャ番号
    global mipmap # ミップマップ番号
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH)
    glutInitWindowSize(300, 300)
    glutCreateWindow(u"Sample 9")
    glutDisplayFunc(display_func)
    glutReshapeFunc(reshape_func)
    glEnable(GL_DEPTH_TEST)

    ppm_path = os.path.join(os.path.dirname(__file__), u"texture2.ppm")
    tex = ppm2texture(ppm_path)
    mipmap = ppm2mipmap(ppm_path)

    glutMainLoop()

if __name__ == '__main__':
    main()