PyOpenGL 8

OpenGLによるオブエジェクの選択

OpenGLでの選択処理は、GL_SELECTモードでポリゴンを描画することによって行われます。
指定された領域にポリゴンが描画されたかどうかで、選択範囲に入っているかを判断します。 ただし、GL_SELECTモードでは実際の描画は行われません。

PyOpenGLで、選択されたポリゴンを調べる手順は以下のようになります。

  1. セレクションバッファ設定
    sel = glSelectBuffer(バッファの個数)
  2. セレクションモードに入る
    glRenderMode(GL_SELECT)
  3. セレクションバッファの初期化
    glInitNames()
    glPushName(0)
  4. ビューポートの初期化
    glMatrixMode(GL_PROJECTION)
    glPushMatrix()
    glLoadIdentity()
  5. 選択範囲の指定
    gluPickMatrix(選択範囲中心X座標, 選択範囲中心Y座標, 選択範囲横幅, 選択範囲縦幅, ビュー情報)
    この後、現在と同じ設定でビューの設定を行います。
  6. ポリゴンに番号を付けながら描画
    番号を変えながら、以下の1,2を繰り返します。
    1. glLoadName(番号)
    2. ポリゴンを1つ描画
  7. ビューの行列を元に戻す
    glMatrixMode(GL_PROJECTION)
    glPopMatrix()
  8. 選択されたポリゴンを得る
    hits = glRenderMode(GL_RENDER)
    リストhitsに選択されたポリゴンの情報が入っています。

マウスによる選択

マウスの左ボタンでクリックされたポリゴンの色を変えてみます。

#!env python

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


vertex = [
    [ -1.0, -1.0, 1.0 ],
    [ 1.0, -1.0, 1.0 ],
    [ -1.0, 1.0, 1.0 ],
    [ 1.0, 1.0, 1.0 ],
    [ -1.0, 1.0, -1.0 ],
    [ 1.0, 1.0, -1.0 ],
    [ -1.0, -1.0, -1.0 ],
    [ 1.0, -1.0, -1.0 ]]

face = [
    [ 0, 1, 3, 2 ],
    [ 2, 3, 5, 4 ],
    [ 4, 5, 7, 6 ],
    [ 6, 7, 1, 0 ],
    [ 1, 7, 5, 3 ],
    [ 6, 0, 2, 4 ]]

color = [
    [ 0.3, 0.3, 0.3 ],
    [ 0.4, 0.4, 0.4 ],
    [ 0.5, 0.5, 0.5 ],
    [ 0.6, 0.6, 0.6 ],
    [ 0.7, 0.7, 0.7 ],
    [ 0.8, 0.8, 0.8 ]]

angleX = 0.0
angleY = 0.0


def resize(w, h):
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(30.0, w/h, 1.0, 100.0)
    glMatrixMode(GL_MODELVIEW)


def drawObjs(name=False):
    global angleX, angleY

    glLoadIdentity()
    gluLookAt(3.0, 4.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
    glRotated(angleX, 1.0, 0.0, 0.0);
    glRotated(angleY, 0.0, 1.0, 0.0);

    for j in range(0, len(face)):
        if name:
            glLoadName(j)
        glBegin(GL_QUADS)
        glColor3dv(color[j])
        for i in range(0, 4):
            glVertex(vertex[face[j][i]])
        glEnd()


def draw():
    glClearColor(0.0, 0.0, 1.0, 0.0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    drawObjs()

    glFlush()
    glutSwapBuffers()


def mouse(button, state, x, y):
    if state == GLUT_DOWN:
        if button == GLUT_LEFT_BUTTON:
            sel = glSelectBuffer(8)
            glRenderMode(GL_SELECT)
            glInitNames()
            glPushName(0)

            glMatrixMode(GL_PROJECTION)
            glPushMatrix()
            glLoadIdentity()
            vp = glGetIntegerv(GL_VIEWPORT)
            gluPickMatrix(x, vp[3] - y -1, 1, 1, vp)
            gluPerspective(30.0, vp[2]/vp[3], 1.0, 100.0)
            glMatrixMode(GL_MODELVIEW)
            drawObjs(name=True)
            glMatrixMode(GL_PROJECTION)
            glPopMatrix()

            hits = glRenderMode(GL_RENDER)
            for n in hits:
                h = n.names[0]
                color[h] = [1.0, 0, 0]
            glMatrixMode(GL_MODELVIEW)
            glutPostRedisplay()


def keyboard(key, x, y):
    global angleX, angleY
    if key=='q':
        sys.exit()
    elif key=='h':
        angleY += 1.0
        glutPostRedisplay()
    elif key=='j':
        angleX += 1.0
        glutPostRedisplay()

glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(320, 320)
glutCreateWindow("PyOpenGL 17")
glutDisplayFunc(draw)
glutReshapeFunc(resize)
glutKeyboardFunc(keyboard)
glutMouseFunc(mouse)

glClearColor(0.0, 0.0, 1.0, 0.0)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)

glutMainLoop()

スクリプト解説

def drawObjs(name=False):
ポリゴン描写のための関数です。
引数の name 変数は、False で初期化されています。
if name:
  glLoadName(j)
name が True の時に、描画するポリゴンに j という数値で名前を付けます。
ここでは、わかりやすくするためにポリゴンの順番と同じ番号を名前にしています。
sel = glSelectBuffer(8)
選択されたオブジェクトのデータを入れておくためのバッファを用意します。
引数は、用意するバッファの個数です。
(ただし、ここではバッファを使用していません。)
glRenderMode(GL_SELECT)
選択モードに入ります。
glInitNames()
名前スタックを初期化します。
glPushName(0)
名前スタックの最初の名前を 0 にしておきます。
名前バッファは階層構造を表せるようになっていますが、 ここでは描画オブジェクトが1つしかないので仮の名前を入れておきます。
glMatrixMode(GL_PROJECTION)
選択の処理は視点座標系で行います。
glPushMatrix()
現在の透視変換行列を保存します。
glLoadIdentity()
透視変換行列を初期化します。
vp = glGetIntegerv(GL_VIEWPORT)
現在のビューポート設定を得ます。
返り値(vp)には、ビューポートのX座標(vp[0])、Y座標(vp[1])、 横幅(vp[2])、縦幅(vp[3])が入っています。
gluPickMatrix(x, vp[3] - y -1, 1, 1, vp)
表示領域がマウスがクリックされた周囲だけになるように変換行列を設定します。
1番目の引数
表示領域のX座標(スクリーン座標系)
2番目の引数
表示領域のY座標(スクリーン座標系)
マウス座標系は、スクリーン座標系に対して上下が反転しているので 補正します。
3番目の引数
表示領域の範囲(X方向)
4番目の引数
表示領域の範囲(Y方向)
5番目の引数
ビューポート設定
gluPerspective(30.0, vp[2]/vp[3], 1.0, 100.0)
透視変換行列を設定します。
vp[2]/vp[3] によって、アスペクト比の計算しています。
glMatrixMode(GL_MODELVIEW)
モデルビュー行列に切替えます。
drawObjs(name=True)
オブジェクトを描画します。
name を True にして、名前を付けるようにします。
glMatrixMode(GL_PROJECTION)
透視変換に戻します。
glPopMatrix()
透視変換行列を元に戻します。
hits = glRenderMode(GL_RENDER)
レンダリングモードを元に戻します。
for n in hits:
選択されたオブジェクトを一つずつ処理します。
n には、選択されたオブジェクトの情報が入っています。
n.names
選択されたオブジェクトの番号のリスト
n.near
デプスバッファの最小値
n.far
デプスバッファの最大値
h = n.names[0]
選択されたポリゴンの番号を h に代入します。
color[h] = [1.0, 0, 0]
h 番目のポリゴンの色を赤色にします。
glMatrixMode(GL_MODELVIEW)
モデルビュー行列に戻します。

オブジェクト名の階層

GL_SELECTモードで、名前に階層構造を与えたいときにはglPushName(), glPopName()を使用します。

glPushName(1)
glPushName(1)
glPushName(0)
glLoadName(0)
# ポリゴンA1を描画
glLoadName(1)
# ポリゴンA2を描画
glPopName()
glPopName()
glPushName(2)
glPushName(0)
glLoadName(0)
# ポリゴンB1を描画
glLoadName(1)
# ポリゴンB2を描画
glPopName()
glPopName()
glPopName()
glPushName(2)
glPushName(1)
glPushName(0)
glLoadName(0)
# ポリゴンC1を描画
glLoadName(1)
# ポリゴンC2を描画
glPopName()
glPopName()
glPopName()

上のコードで、選択されたオブジェクトの名前は以下のようになります。
マウスの選択スクリプトでは、n.namesに入っている値です。

ポリゴンA1, A2
[1, 1, 0], [1, 1, 1]
ポリゴンB1, B2
[1, 2, 0], [1, 2, 1]
ポリゴンC1, C2
[2, 1, 0], [2, 1, 1]

練習問題

  1. 前回の練習問題を参考にして、マウス左ボタンのクリックによって、選択されたポリゴンの色を変更するようにしてみましょう。

    # python objdisplay7.py sphere.obj

    ポリゴンが選択されたとき

参考


Prev | Next
index | home
abetmhr@gmail.com