人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

プリミティブの描画

プリミティブを描くサンプルです。プリミティブとはOpenGLで使える図形のことで、点、線、三角形、四角形、ポリゴンなどがあります。

primitive.py
f:id:aidiary:20100814102836p:plain

サンプルスクリプト

#!/usr/bin/env python
#coding:utf-8
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import sys

def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE)
    glutInitWindowSize(300, 300)
    glutInitWindowPosition(100, 100)
    glutCreateWindow("")
    glutDisplayFunc(display)
    init()
    glutMainLoop()

def init():
    glClearColor(0.0, 0.0, 1.0, 1.0)
    
    # 座標系の設定
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(-1.0, 1.0, -1.0, 1.0)

def display():
    """描画処理"""
    glClear(GL_COLOR_BUFFER_BIT)
    
    # 赤い四角形を描く
    glColor3f(1.0, 0.0, 0.0)
    glBegin(GL_QUADS)
    glVertex2f(-0.5, -0.5)
    glVertex2f(-0.5, 0.5)
    glVertex2f(0.5, 0.5)
    glVertex2f(0.5, -0.5)
    glEnd()
    
    glFlush()

if __name__ == "__main__":
    main()

座標系の設定と描画

図形を書くためにはまず座標系を設定する必要があります。Pygameなどでは、ウィンドウの左上が(0,0)で右下が(width,height)で単位はピクセルと決まっていますが、OpenGLでは自由に設定できます。

    # 座標系の設定
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(-1.0, 1.0, -1.0, 1.0)

OpenGLは3次元の空間を扱えますが、とりあえず2次元の平面で考えます。2次元の座標系の設定はgluOrtho2D()で設定します。gluはGLUというライブラリの関数です。ややこしいですがGLUTとはまた別です。gluOrtho2Dの引数は、

  gluOrtho2D(left, right, bottom, top)

です。(left, bottom)が画面の左下の座標、(right,top)が画面の右上の座標になります。例では、下図のように左下が(-1.0,-1.0)、右上が (1.0,1.0)になるように座標系を設定してます。この座標系はデフォルトなので何も指定しないとこれになります。

f:id:aidiary:20100814102837p:plain

次にこの座標上に四角形を描画します。

    # 赤い四角形を描く
    glColor3f(1.0, 0.0, 0.0)
    glBegin(GL_QUADS)
    glVertex2f(-0.5, -0.5)
    glVertex2f(-0.5, 0.5)
    glVertex2f(0.5, 0.5)
    glVertex2f(0.5, -0.5)
    glEnd()

四角形のような図形をプリミティブと言いますが、プリミティブを描くときはglBegin()とglEnd()で囲み、その間に頂点の座標をだらだら書いていきます。glBegin()の引数にはどのプリミティブを書こうとしているのか指定します。四角形はGL_QUADSです。四角形は英語でQuadrangleなのでその略でQUADS。四角形と言えばSquareですが、Squareは辺の長さが全部等しい正方形みたいです。頂点はglVertex2f()で4個指定します。2fは引数にfloat型(小数)を2つ与えるという意味です。引数はX座標とY座標です。座標系は自由に設定できるので、たとえば、

    # 座標系の設定
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0 100, 0, 100)

とすることもできます(下図)。

f:id:aidiary:20100814102838p:plain

左下が(0,0)、右上が(100,100)です。この座標系で同じような四角形を描くときは、

    # 赤い四角形を描く
    glColor3f(1.0, 0.0, 0.0)
    glBegin(GL_QUADS)
    glVertex2i(25, 25)
    glVertex2i(25, 75)
    glVertex2i(75, 75)
    glVertex2i(75, 25)
    glEnd()

となります。2iは引数にint型(整数)を2つ与えるという意味です。

頂点色の設定

四角形を描く前に

  glColor3f(1.0, 0.0, 0.0)

と指定しました。これは、頂点の色を設定するコマンドです。引数は (R,G,B) です。この場合、赤にしているのでそれ以後に描画される頂点はすべて赤になり、結果的に四角形は赤でべた塗りされます。下のように頂点を描画する直前で色を変えてやると頂点の色を変えることができます。中身は頂点の色にしたがってグラデーションがかかりますが、こういうのをスムーズシェーディングといいます。

    glBegin(GL_QUADS)
    glColor3f(1.0, 0.0, 0.0)  # 赤
    glVertex2f(-0.5, -0.5)
    glColor3f(0.0, 1.0, 0.0)  # 緑
    glVertex2f(-0.5, 0.5)
    glColor3f(0.0, 0.0, 1.0)  # 青
    glVertex2f(0.5, 0.5)
    glColor3f(1.0, 1.0, 0.0)  # 黄色
    glVertex2f(0.5, -0.5)
    glEnd()

f:id:aidiary:20100814102839p:plain

点の描画(GL_POINTS)

点の描画は、glBegin()にGL_POINTSを指定します。点のサイズは、glPointSize()で指定します。デフォルトのサイズは1.0です。

    # 点の描画
    glColor3f(1.0, 1.0, 0.0)  # 黄色
    glPointSize(3.0)  # 点のサイズ
    glBegin(GL_POINTS)
    glVertex2f(-0.5, -0.5)
    glVertex2f(-0.5, 0.5)
    glVertex2f(0.5, 0.5)
    glVertex2f(0.5, -0.5)
    glEnd()

f:id:aidiary:20100814102840p:plain

線の描画(GL_LINES)

線の描画は、GL_LINES、GL_LINE_STRIP、GL_LINE_LOOPの3種類の方法があります。GL_LINESは2つずつ頂点を組み合わせて線を引きます。下の場合、AB間で1本、CD間で1本の線を引きます。線の太さは、glLineWidth()で設定します。デフォルトは1.0です。

    # 線の描画(GL_LINES)
    glColor3f(1.0, 1.0, 0.0)  # 黄色
    glLineWidth(3.0)  # 線のサイズ
    glBegin(GL_LINES)
    glVertex2f(-0.5, -0.5)  # A
    glVertex2f(-0.5, 0.5)   # B
    glVertex2f(0.5, 0.5)    # C
    glVertex2f(0.5, -0.5)   # D
    glEnd()

f:id:aidiary:20100814102841p:plain

線の描画(GL_LINE_STRIP)

GL_LINE_STRIPは1つ前の線の終点が次の線の始点になります。下の場合、AB間、BC間、CD間で合計3本の線が引かれます。

    # 線の描画(GL_LINE_STRIP)
    glColor3f(1.0, 1.0, 0.0)  # 黄色
    glLineWidth(3.0)  # 線のサイズ
    glBegin(GL_LINE_STRIP)
    glVertex2f(-0.5, -0.5)  # A
    glVertex2f(-0.5, 0.5)   # B
    glVertex2f(0.5, 0.5)    # C
    glVertex2f(0.5, -0.5)   # D
    glEnd()

f:id:aidiary:20100814102842p:plain

線の描画(GL_LINE_LOOP)

GL_LINE_LOOPは、GL_LINE_STRIPとほとんど同じですが、終点と始点も結ばれます。下の場合、AB間、BC間、CD間に加えてDA間にも線が引かれます。

    # 線の描画(GL_LINE_LOOP)
    glColor3f(1.0, 1.0, 0.0)  # 黄色
    glLineWidth(3.0)  # 線のサイズ
    glBegin(GL_LINE_LOOP)
    glVertex2f(-0.5, -0.5)  # A
    glVertex2f(-0.5, 0.5)   # B
    glVertex2f(0.5, 0.5)    # C
    glVertex2f(0.5, -0.5)   # D
    glEnd()

f:id:aidiary:20100814102843p:plain

三角形の描画(GL_TRIANGLES)

三角形は、GL_TRIANGLESです。頂点を3つずつ組にして三角形にします。三角形は他にも頂点を共有するGL_TRIANGLE_STRIP、GL_TRIANGLE_FANというのがあります。

    # 三角形の描画(GL_TRIANGLE)
    glColor3f(0.0, 1.0, 1.0)
    glBegin(GL_TRIANGLES)
    glVertex2f(0.0, 0.75)
    glVertex2f(-0.75, -0.75)
    glVertex2f(0.75, -0.75)
    glEnd()

f:id:aidiary:20100814102844p:plain

四角形の描画(GL_QUADS)

四角形は、GL_QUADSです。頂点を4つずつ組にして四角形にします。

    # 四角形の描画(GL_QUADS)
    glColor3f(1.0, 0.0, 1.0)
    glBegin(GL_QUADS)
    glVertex2f(-0.5, -0.5)
    glVertex2f(-0.5, 0.5)
    glVertex2f(0.5, 0.5)
    glVertex2f(0.5, -0.5)
    glEnd()

f:id:aidiary:20100814102845p:plain

ポリゴンの描画(GL_POLYGON)

ポリゴンっていうと三角形を思い浮かべてしまいますが、多角形のことです。ポリゴンは、GL_POLYGONです。三角形や四角形もポリゴンの一種なのでGL_POLYGONで描画できますが、専用のGL_TRIANGLESやGL_QUADSを使った方が描画が高速だそうです。

    # ポリゴンの描画(GL_POLYGON)
    glColor3f(1.0, 0.0, 0.0)
    glBegin(GL_POLYGON)
    glVertex2f(0.0, 0.75)
    glVertex2f(-0.5, 0.5)
    glVertex2f(-0.5, -0.5)
    glVertex2f(0.0, -0.75)
    glVertex2f(0.5, -0.5)
    glVertex2f(0.5, 0.5)
    glEnd()

f:id:aidiary:20100814102846p:plain

ポリゴンの表と裏

f:id:aidiary:20100814102847p:plain

ポリゴンの頂点には表と裏があります。画面に向かったとき、反時計周りに頂点を指定した面は表で時計周りに頂点を指定した面は裏です。ポリゴンの表と裏をそれぞれどのようにレンダリングするかはglPolygonMode()で決められます。下の例では、表側は塗りつぶし、裏側は線で描画しています。第一引数には、GL_FRONT、GL_BACK、GL_FRONT_AND_BACKのいずれか、第二引数には、GL_POINT、 GL_LINE、GL_FILLのいずれかが指定できます。デフォルトでは、表裏ともGL_FILLなので塗りつぶされます。

    # 面のレンダリング方法を指定
    glPolygonMode(GL_FRONT, GL_FILL) # 表は塗りつぶす
    glPolygonMode(GL_BACK, GL_LINE)  # 裏は線
    glColor3f(1.0, 1.0, 0.0)
    glBegin(GL_QUADS)
    # 反時計周り(表)に頂点を指定した四角形
    glVertex2f(-0.9, -0.4)  # A
    glVertex2f(-0.1, -0.4)  # B
    glVertex2f(-0.1, 0.4)   # C
    glVertex2f(-0.9, 0.4)   # D
    # 時計周り(裏)に頂点を指定した四角形
    glVertex2f(0.1, -0.4)   # E
    glVertex2f(0.1, 0.4)    # F
    glVertex2f(0.9, 0.4)    # G
    glVertex2f(0.9, -0.4)   # H
    glEnd()

反時計周りと時計周りのどちらをポリゴンの表とするかは、glFrontFace()で変えられます。

  glFrontFace(GL_CCW)  # 反時計周りを表にする(デフォルト)
  glFrontFace(GL_CW)    # 時計回りを表にする

カリング

f:id:aidiary:20100814102849p:plain

ポリゴンの表または裏をレンダリングしない(=カリング)ようにも設定できます。glCullFace()の引数にGL_FRONT、 GL_BACK、GL_FRONT_AND_BACKのいずれかを指定すると指定した面がレンダリングされなくなります。例では裏面をカリングしました。カリングを有効にするにはglEnable(GL_CULL_FACE)を実行する必要があります。

    # カリング
    glEnable(GL_CULL_FACE)  # カリングを有効に
    glCullFace(GL_BACK)  # 裏面をカリング
    glColor3f(1.0, 1.0, 0.0)
    glBegin(GL_QUADS)
    # 反時計周り(表)に頂点を指定した四角形
    glVertex2f(-0.9, -0.4)  # A
    glVertex2f(-0.1, -0.4)  # B
    glVertex2f(-0.1, 0.4)   # C
    glVertex2f(-0.9, 0.4)   # D
    # 時計周り(裏)に頂点を指定した四角形
    glVertex2f(0.1, -0.4)   # E
    glVertex2f(0.1, 0.4)    # F
    glVertex2f(0.9, 0.4)    # G
    glVertex2f(0.9, -0.4)   # H
    glEnd()