2008/05/12

Multiple Texture

テクスチャーを2種類使ったサンプルです。どうやら次のような手順で2種類のテクスチャーを使うようです。
Sample procedure for using two texture.

#設定:一度やればいいようです。
#Setting parameters - you shoud do this only once
glEnable(GL_TEXTURE_2D)
glEnable(GL_TEXTURE_GEN_S)
glEnable(GL_TEXTURE_GEN_T)
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)

#テクスチャーの空き番号を必要個数取得します。
# get multiple available texture ID
texture=glGenTextures(2)

#ひとつ目のテクスチャーに設定
# set the first texture as current texture
glBindTexture(GL_TEXTURE_2D, texture[0])

# 画像ファイルから数列にテクスチャデータを読み込みます
# read data from graphic file and store data in image.tostring()
image=Image.open("ksmt.bmp")
if len(image.getbands())!=4:
image=image.convert("RGBA")
size=image.size

# image.tostring()という数列のデータを使ってテクスチャーを作ります
# generate texture from image.tostring()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image.tostring())

# 意味不明ですが、glTexImage2Dの後に次の2行がないとうまくテクスチャ ができません。
# I don't know why I should do this every time after glTexImage2D, but I must do this anyway.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

# 二つ目のテクスチャに設定します。
# set the second texture as current texture
glBindTexture(GL_TEXTURE_2D, texture[1])

# ファイル(この場合TIF)から数列に画像データを読み込みます
# read data from the second file to memory
image=Image.open("f.tif")
if len(image.getbands())!=4:
image=image.convert("RGBA")
size=image.size

# 数列からテクスチャを作ります
# generate the second texture from memory
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image.tostring())

# 再びこの2行が必要です。どうやらテクスチャごとに必要のようです。
# I must do this again. I don't know the reason. This looks like a paramater setting.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

#Display Funcの中でテクスチャを指定します。
#inside Display function
glDisable(GL_TEXTURE_2D) #テクスチャ不要の場合 no texture
#図形描画 #テクスチャなし図形 instanciate object with no texture
glEnable(GL_TEXTURE_2D) #テクスチャ貼り付け enable texture mapping
glBindTexture(GL_TEXTURE_2D, texture[0]) #ひとつ目のテクスチャを使う use the first texture
# 図形描画 #ひとつ目のテクスチャ instance object with the first texture
glBindTexture(GL_TEXTURE_2D, texture[0]) #ふたつ目のテクスチャを使う secont texture
#図形描画 #ふたつ目のテクスチャ instance object with the second texture

GL_EYE_LINEAR

テクスチャー座標自動生成には2つのオプションがあります。GL_EYE_LINEARをセットすると、テクスチャーの位置がカメラに固定されます。図形が動くと模様が変わります。例えば、スライド上映中にスクリーンの前を横切った人のようなものです。あるいは、通り過ぎる鏡面仕上げの車に映るあなたの姿のようなものです。ただあまり大きなテクスチャーは大きな負荷になるそうですので、用途は限られそうです。
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)

一方、GL_OBJECT_LINEARを指定すると、図形と一緒に模様が動きます。テクスチャー柄の洋服を着た人が動き回るようなものです。これは普通にテクスチャですね。
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)

Automatic texture coordinate generation has two options. GL_EYE_LINEAR fixes relative position of texture and camera. This is something like you walk across a projection screen during slide show.
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)

GL_OBJECT_LINEAR fixed texture potition to the object. When object moves, texture moves as a skin of the object.
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)

現時点でのソースコード:

import sys
import math
import Image
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

TORSO_RADIUS=0.1
TORSO_HEIGHT=0.4

HEAD_RADIUS = 0.1

UPPER_ARM_HEIGHT=0.2
UPPER_ARM_WIDTH=0.07

LOWER_ARM_HEIGHT=0.2
LOWER_ARM_WIDTH=0.05

UPPER_LEG_HEIGHT=0.2
UPPER_LEG_WIDTH=0.08

LOWER_LEG_HEIGHT=0.2
LOWER_LEG_WIDTH=0.06

SHOLDER_WIDTH = 0.2
HIP_WIDTH = 0.2

HEADX=0.1
HEADY=TORSO_HEIGHT

LUAX=-1.0 * SHOLDER_WIDTH / 2
RUAX=SHOLDER_WIDTH / 2
LUAY=RUAY=TORSO_HEIGHT
LLAY=RLAY=LOWER_ARM_HEIGHT

LULX=-1.0 * HIP_WIDTH / 2
RULX=HIP_WIDTH / 2
LULY=RULY=0
LLLY=RLLY=LOWER_LEG_HEIGHT

t0=0.0
t1=0.0
t2=0.0
t3=90.0
t4=0.0
t5=90.0
t6=0.0
t7=180.0
t8=0.0
t9=180.0
t10=0.0

def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()

# set camera FOV=20 degree, aspect=1.0, near=1.0(0.0 will cause error), far=100.9
gluPerspective(20, 1.0, 1.0, 100.0)

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Place camera at (2,2,2), to direction (0, 0.2, 0), rotation around Z axis 0 degree(up Y)
gluLookAt(2.0, 2.0, 2.0, 0.0, 0.2, 0.0, 0.0, 1.0, 0.0)

#TORSO
#glDisable(GL_TEXTURE_2D)
gray()
materials(brass_amb, brass_diff, brass_spec, brass_shin)
glRotatef(t0, 0.0, 1.0, 0.0)
torso()
glPushMatrix()

#HEAD
cyan()
glTranslatef(0.0, HEADX, 0.0)
glRotatef(t1, 1.0, 0.0, 0.0)
glRotatef(t2, 0.0, 1.0, 0.0)
glTranslatef(0.0, HEADY, 0.0)
head()

#nose
yellow()
glTranslatef(0.0, 0.0, HEAD_RADIUS)
#glRotatef(0, 1.0, 0.0, 0.0)
nose()

#eyes
magenta()
glTranslatef(HEAD_RADIUS/2, HEAD_RADIUS/2, 0.0)
#glRotatef(0, 1.0, 0.0, 0.0)
nose()

glTranslatef(-HEAD_RADIUS, 0.0, 0.0)
#glRotatef(0, 1.0, 0.0, 0.0)
nose()

#LEFT UPPER ARM
glEnable(GL_TEXTURE_2D)
glPopMatrix()
glPushMatrix()
red()
glTranslatef(LUAX, LUAY, 0.0)
glRotatef(t3, 1.0, 0.0, 0.0)
upper_arm()

#LEFT LOWER ARM
green()
glTranslatef(0.0, LLAY, 0.0)
glRotatef(t4, 1.0, 0.0, 0.0)
lower_arm()

#RIGHT UPPER ARM
glPopMatrix()
glPushMatrix()
blue()
glTranslatef(RUAX, RUAY, 0.0)
glRotatef(t5, 1.0, 0.0, 0.0)
upper_arm()

#RIGHT UPPER ARM
cyan()
glTranslatef(0.0, RLAY, 0.0)
glRotatef(t6, 1.0, 0.0, 0.0)
lower_arm()

#LEFT UPPER LEG
glPopMatrix()
glPushMatrix()
magenta()
glTranslatef(LULX, LULY, 0.0)
glRotatef(t7, 1.0, 0.0, 0.0)
upper_leg()

#LEFT LOWER LEG
yellow()
glTranslatef(0.0, LLLY, 0.0)
glRotatef(t8, 1.0, 0.0, 0.0)
lower_leg()

#RIGHT UPPER LEG
glPopMatrix()
glPushMatrix()
pink()
glTranslatef(RULX, RULY, 0.0)
glRotatef(t9, 1.0, 0.0, 0.0)
upper_leg()

#RIGHT LOWER LEG
gray()
glTranslatef(0.0, RLLY, 0.0)
glRotatef(t10, 1.0, 0.0, 0.0)
lower_leg()

glPopMatrix()
#glutSwapBuffers()
glFlush()

def torso():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
gluCylinder(p, TORSO_RADIUS, TORSO_RADIUS, TORSO_HEIGHT, 10, 10)
glPopMatrix()

def head():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
glutSolidSphere(HEAD_RADIUS, 10, 10)
glPopMatrix()

def upper_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(UPPER_ARM_WIDTH, UPPER_ARM_HEIGHT, UPPER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(LOWER_ARM_WIDTH, LOWER_ARM_HEIGHT, LOWER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def upper_leg():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(UPPER_LEG_WIDTH, UPPER_LEG_HEIGHT, UPPER_LEG_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_leg():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(LOWER_LEG_WIDTH, LOWER_LEG_HEIGHT, LOWER_LEG_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def nose():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
glutSolidSphere(HEAD_RADIUS/5, 10, 10)
glPopMatrix()

def red():
glColor3f(1.0, 0.0, 0.0)
def green():
glColor3f(0.0, 1.0, 0.0)
def blue():
glColor3f(0.0, 0.0, 1.0)
def cyan():
glColor3f(0.0, 1.0, 1.0)
def magenta():
glColor3f(1.0, 1.0, 0.0)
def yellow():
glColor3f(1.0, 0.0, 1.0)
def pink():
glColor3f(1.0, 0.5, 0.5)
def gray():
glColor3f(0.7, 0.7, 0.7)

def mykey(key, x, y):
global t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
if key=='d': # TORSO
t0 = t0 + 10.0
elif key=='D':
t0 = t0 - 10.0
elif key=='e': # HEAD 1
t1 = t1 + 10.0
elif key=='E':
t1 = t1 - 10.0
elif key=='r': # HEAD 2
t2 = t2 + 10.0
elif key=='R':
t2 = t2 - 10.0
elif key=='s': # LUA
t3 = t3 + 10.0
elif key=='S':
t3 = t3 - 10.0
elif key=='a': # LLA
t4 = t4 + 10.0
elif key=='A':
t4 = t4 - 10.0
elif key=='f': # RUA
t5 = t5 + 10.0
elif key=='F':
t5 = t5 - 10.0
elif key=='g': # RLA
t6 = t6 + 10.0
elif key=='G':
t6 = t6 -10.0
elif key=='x': # LUL
t7 = t7 + 10.0
elif key=='X':
t7 = t7 - 10.0
elif key=='z': # LLL
t8 = t8 + 10.0
elif key=='Z':
t8 = t8 -10.0
elif key=='c': # RUL
t9 = t9 + 10.0
elif key=='C':
t9 = t9 - 10.0
elif key=='v': # RLL
t10 = t10 + 10.0
elif key=='V':
t10 = t10 - 10.0
elif key=='q':
sys.exit()
elif key=='Q':
sys.exit()

print "params: ", t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
glutPostRedisplay()

def materials(amb, diff, spec, shin):
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb)
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diff)
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec)
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shin)

glutInit( sys.argv )
#glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH )
glutInitDisplayMode( GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'robot' )
glutDisplayFunc( display )
glutKeyboardFunc(mykey)

glEnable(GL_DEPTH_TEST) #hidden-usrface removal
glEnable(GL_CULL_FACE) #don't draw back side surface
glCullFace(GL_BACK) #don't draw back side surface

### TEXTURE
glEnable(GL_TEXTURE_2D)
# generate continuous one texture
# different format from C, void glGenTexture(n, *name)
texture=glGenTextures(1)

# use the generated texture
glBindTexture(GL_TEXTURE_2D, texture)

# read texture data from .bmp file ising Image library
image=Image.open("ksmt.bmp")
if len(image.getbands())!=4:
image=image.convert("RGBA")
size=image.size

# generate texture from array image.tostring() inside current texture 'texture'
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image.tostring())

# set texture options, repeat same pattern
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)

# set texture options, enlarge and shrink
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

# set texture options, slow but good image
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)

# enable auto generation of texture coordinate in s and t direction
glEnable(GL_TEXTURE_GEN_S)
glEnable(GL_TEXTURE_GEN_T)

# setup OBJECT_LINEAR ot EYE_LINEAR for auto texture coord generation
#glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
#glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)

# Not successfull yet
#planes = 0.5, 0.0, 0.0, 0.5
#planet = 0.0, 0.5, 0.0, 0.5
#glTexGenfv(GL_S, GL_OBJECT_LINEAR, planes)
#glTexGenfv(GL_T, GL_OBJECT_LINEAR, planet)

### Lighting
glEnable(GL_LIGHTING) #use lighting
glEnable(GL_LIGHT0) #use a light 0
glEnable(GL_LIGHT1) #use a light 1

# light 0
light0_pos = 10.0, 10.0, 10.0, 0.0
diffuse0 = 0.5, 0.5, 0.5, 1.0
specular0 = 0.5, 0.5, 0.5, 1.0
ambient0 = 0.8, 0.8, 0.8, 1.0

glMatrixMode(GL_MODELVIEW)
glLightfv(GL_LIGHT0, GL_POSITION, light0_pos)
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0)
glLightfv(GL_LIGHT0, GL_SPECULAR, specular0)
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0)

# light 1
light1_pos = 0.0, 0.0, 10.0, 0.0
diffuse1 = 0.8, 0.8, 0.8, 1.0
specular1 = 0.2, 0.2, 0.2, 1.0
ambient1 = 0.2, 0.2, 0.2, 1.0

glLightfv(GL_LIGHT1, GL_POSITION, light1_pos)
glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1)
glLightfv(GL_LIGHT1, GL_SPECULAR, specular1)
glLightfv(GL_LIGHT1, GL_AMBIENT, ambient1)

# Material
brass_amb = 0.33, 0.22, 0.03, 1.0
brass_diff = 0.78, 0.57, 0.11, 1.0
brass_spec = 0.99, 0.91, 0.81, 1.0
brass_shin = 27.8

p_amb = 0.3, 0.0, 0.0, 1.0
p_diff = 0.6, 0.0, 0.0, 1.0
p_spec = 0.8, 0.6, 0.6, 1.0
p_shin = 32.8

# create new Quadric 'p' for gluCylinder and set texture for it
p=gluNewQuadric()
gluQuadricDrawStyle(p, GLU_FILL)
gluQuadricNormals(p, GLU_SMOOTH)
#gluQuadricTexture(p, GL_TRUE)

glutMainLoop()

TEXTURE is difficult to understand

まったくテクスチャマッピングというのはさっぱり分かりません。入門書の内容が急に難解になり、分かりやすいサンプルがついていません。特にgluあるいはglutオブジェクトにどうやってテクスチャを貼るのかが分かりません。本に書いてあるようなのですが、例がないので理解できないのです。また、Webをいくら探してもPythonでglutソリッドモデルにテクスチャを貼った例が見つからないのです。全部glTexCoord2fでポリゴンにマップしています。
で、まる1日四苦八苦して、やっとなんとかテクスチャが貼れるようになったのですが、相変わらず理解できません。絵もコードもぐちゃぐちゃです。でも、まあ絵がでただけましで、これで前に進めると思います。

I cannot understand Texture Mapping yet. It's really complex and difficult to understand. I cannot find good example in books nor web. Most examples use glTexCoord2f. This is good for polygons, but not good for glu and glut solid models I'm using today.

After a whole day struggling on Texture, I finally can see something on screen. But, I still don't understand what happening. I can probably go further by looking at the pictures.

Followings are my findings yesterday and today on Texture Mapping.

1. Image library is usefull for reading Texture from graphic files. Install Image library from http://www.pythonware.com/products/pil/ on Python and "import Image".

2. Read a .bmp file into a matrix and specify the matrix as Texture.

image=Image.open("ksmt.bmp")
if len(image.getbands())!=4:
image=image.convert("RGBA")
size=image.size
# generate texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image.tostring())

3. Enable using 2D texture
glEnable(GL_TEXTURE_2D)

4. Enable? texture for gluQuadric object

p=gluNewQuadric()
gluQuadricDrawStyle(p, GLU_FILL)
gluQuadricNormals(p, GLU_SMOOTH)
gluQuadricTexture(p, GL_TRUE)

5. Enable auto texture coordinate generation

glEnable(GL_TEXTURE_GEN_S)
glEnable(GL_TEXTURE_GEN_T)
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR)

Something wrong in using glTexGenfv. I need to understand this
planes = 0.5, 0.0, 0.0, 0.5
planet = 0.0, 0.5, 0.0, 0.5
#glTexGenfv(GL_S, GL_OBJECT_LINEAR, planes)
#glTexGenfv(GL_T, GL_OBJECT_LINEAR, planet)

2008/05/11

Adding Light and Material to humanoid

ロボットに真鍮の素材を指定し、照明を当ててみました。大分それらしくなってきましたね。
I added material as brass and I gave a light to the simple humanoid robot.

ちょっとコードが長くなったのですが、とりあえずそのまま貼ります。


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

TORSO_RADIUS=0.1
TORSO_HEIGHT=0.4

HEAD_RADIUS = 0.1

UPPER_ARM_HEIGHT=0.2
UPPER_ARM_WIDTH=0.07

LOWER_ARM_HEIGHT=0.2
LOWER_ARM_WIDTH=0.05

UPPER_LEG_HEIGHT=0.2
UPPER_LEG_WIDTH=0.08

LOWER_LEG_HEIGHT=0.2
LOWER_LEG_WIDTH=0.06

SHOLDER_WIDTH = 0.2
HIP_WIDTH = 0.2

HEADX=0.1
HEADY=TORSO_HEIGHT

LUAX=-1.0 * SHOLDER_WIDTH / 2
RUAX=SHOLDER_WIDTH / 2
LUAY=RUAY=TORSO_HEIGHT
LLAY=RLAY=LOWER_ARM_HEIGHT

LULX=-1.0 * HIP_WIDTH / 2
RULX=HIP_WIDTH / 2
LULY=RULY=0
LLLY=RLLY=LOWER_LEG_HEIGHT

t0=0.0
t1=0.0
t2=0.0
t3=90.0
t4=0.0
t5=90.0
t6=0.0
t7=180.0
t8=0.0
t9=180.0
t10=0.0

def display():
glEnable(GL_DEPTH_TEST)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(20, 1.0, 1.0, 100.0)

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(2.0, 2.0, 2.0, 0.0, 0.2, 0.0, 0.0, 1.0, 0.0)

#TORSO
gray()
materials(brass_amb, brass_diff, brass_spec, brass_shin)
glRotatef(t0, 0.0, 1.0, 0.0)
torso()
glPushMatrix()

#HEAD
cyan()
glTranslatef(0.0, HEADX, 0.0)
glRotatef(t1, 1.0, 0.0, 0.0)
glRotatef(t2, 0.0, 1.0, 0.0)
glTranslatef(0.0, HEADY, 0.0)
head()

#nose
yellow()
glTranslatef(0.0, 0.0, HEAD_RADIUS)
#glRotatef(0, 1.0, 0.0, 0.0)
nose()

#eyes
magenta()
glTranslatef(HEAD_RADIUS/2, HEAD_RADIUS/2, 0.0)
#glRotatef(0, 1.0, 0.0, 0.0)
nose()

glTranslatef(-HEAD_RADIUS, 0.0, 0.0)
#glRotatef(0, 1.0, 0.0, 0.0)
nose()

#LEFT UPPER ARM
glPopMatrix()
glPushMatrix()
red()
glTranslatef(LUAX, LUAY, 0.0)
glRotatef(t3, 1.0, 0.0, 0.0)
upper_arm()

#LEFT LOWER ARM
green()
glTranslatef(0.0, LLAY, 0.0)
glRotatef(t4, 1.0, 0.0, 0.0)
lower_arm()

#RIGHT UPPER ARM
glPopMatrix()
glPushMatrix()
blue()
glTranslatef(RUAX, RUAY, 0.0)
glRotatef(t5, 1.0, 0.0, 0.0)
upper_arm()

#RIGHT UPPER ARM
cyan()
glTranslatef(0.0, RLAY, 0.0)
glRotatef(t6, 1.0, 0.0, 0.0)
lower_arm()

#LEFT UPPER LEG
glPopMatrix()
glPushMatrix()
magenta()
glTranslatef(LULX, LULY, 0.0)
glRotatef(t7, 1.0, 0.0, 0.0)
upper_leg()

#LEFT LOWER LEG
yellow()
glTranslatef(0.0, LLLY, 0.0)
glRotatef(t8, 1.0, 0.0, 0.0)
lower_leg()

#RIGHT UPPER LEG
glPopMatrix()
glPushMatrix()
pink()
glTranslatef(RULX, RULY, 0.0)
glRotatef(t9, 1.0, 0.0, 0.0)
upper_leg()

#RIGHT LOWER LEG
gray()
glTranslatef(0.0, RLLY, 0.0)
glRotatef(t10, 1.0, 0.0, 0.0)
lower_leg()

glPopMatrix()
#glutSwapBuffers()
glFlush()

def torso():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
gluCylinder(p, TORSO_RADIUS, TORSO_RADIUS, TORSO_HEIGHT, 10, 10)
glPopMatrix()

def head():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
glutSolidSphere(HEAD_RADIUS, 10, 10)
glPopMatrix()

def upper_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(UPPER_ARM_WIDTH, UPPER_ARM_HEIGHT, UPPER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(LOWER_ARM_WIDTH, LOWER_ARM_HEIGHT, LOWER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def upper_leg():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(UPPER_LEG_WIDTH, UPPER_LEG_HEIGHT, UPPER_LEG_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_leg():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(LOWER_LEG_WIDTH, LOWER_LEG_HEIGHT, LOWER_LEG_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def nose():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
glutSolidSphere(HEAD_RADIUS/5, 10, 10)
glPopMatrix()

def red():
glColor3f(1.0, 0.0, 0.0)
def green():
glColor3f(0.0, 1.0, 0.0)
def blue():
glColor3f(0.0, 0.0, 1.0)
def cyan():
glColor3f(0.0, 1.0, 1.0)
def magenta():
glColor3f(1.0, 1.0, 0.0)
def yellow():
glColor3f(1.0, 0.0, 1.0)
def pink():
glColor3f(1.0, 0.5, 0.5)
def gray():
glColor3f(0.7, 0.7, 0.7)

def mykey(key, x, y):
global t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
if key=='d': # TORSO
t0 = t0 + 10.0
elif key=='D':
t0 = t0 - 10.0
elif key=='e': # HEAD 1
t1 = t1 + 10.0
elif key=='E':
t1 = t1 - 10.0
elif key=='r': # HEAD 2
t2 = t2 + 10.0
elif key=='R':
t2 = t2 - 10.0
elif key=='s': # LUA
t3 = t3 + 10.0
elif key=='S':
t3 = t3 - 10.0
elif key=='a': # LLA
t4 = t4 + 10.0
elif key=='A':
t4 = t4 - 10.0
elif key=='f': # RUA
t5 = t5 + 10.0
elif key=='F':
t5 = t5 - 10.0
elif key=='g': # RLA
t6 = t6 + 10.0
elif key=='G':
t6 = t6 -10.0
elif key=='x': # LUL
t7 = t7 + 10.0
elif key=='X':
t7 = t7 - 10.0
elif key=='z': # LLL
t8 = t8 + 10.0
elif key=='Z':
t8 = t8 -10.0
elif key=='c': # RUL
t9 = t9 + 10.0
elif key=='C':
t9 = t9 - 10.0
elif key=='v': # RLL
t10 = t10 + 10.0
elif key=='V':
t10 = t10 - 10.0
elif key=='q':
sys.exit()

print "params: ", t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
glutPostRedisplay()

def materials(amb, diff, spec, shin):
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb)
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diff)
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec)
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shin)

glutInit( sys.argv )
#glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH )
glutInitDisplayMode( GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'robot' )
glutDisplayFunc( display )
glutKeyboardFunc(mykey)

glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)

glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)

light0_pos = 10.0, 10.0, 10.0, 0.0
diffuse0 = 0.5, 0.5, 0.5, 1.0
specular0 = 0.5, 0.5, 0.5, 1.0
ambient0 = 0.8, 0.8, 0.8, 1.0

glMatrixMode(GL_MODELVIEW)
glLightfv(GL_LIGHT0, GL_POSITION, light0_pos)
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0)
glLightfv(GL_LIGHT0, GL_SPECULAR, specular0)
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0)

p=gluNewQuadric()
gluQuadricDrawStyle(p, GLU_FILL)
gluQuadricNormals(p, GLU_SMOOTH)

brass_amb = 0.33, 0.22, 0.03, 1.0
brass_diff = 0.78, 0.57, 0.11, 1.0
brass_spec = 0.99, 0.91, 0.81, 1.0
brass_shin = 27.8

p_amb = 0.3, 0.0, 0.0, 1.0
p_diff = 0.6, 0.0, 0.0, 1.0
p_spec = 0.8, 0.6, 0.6, 1.0
p_shin = 32.8

glutMainLoop()

2008/05/10

gluPerspective error


ロボットを作っているとき、どうしても隠面消去がうまくいかず、後で描画した図形が常に手前に見えます。原因を調べるのに丸一日かかりました。ああ、疲れた。上のような変な絵が出るのです。
When I made a simple humanoid, I found a problem in hidden-surface-removal. Polygons deawn late is always displayed. I spent whole one day to identify the source of the issue. Strange picture like above is always displayed.

こちらは正常な隠面消去行われた様子です。原因が分かれば簡単でして、何度も読んだ”OpenGL入門”(エドワード・エンジェル著)にも次のように書いてあるのです。
"Alert! 前方クリッピング面にカメラをあまり近づけると、透視ビューに関するデプス計算で数値エラーにつながりかねない。”
This is the right one. "OpenGL, a PRIMER (Edward Angel)" said arithmetic error would heppen in depth calculation when the near clopping surface was too close from camera.

原因はまさにこれだったのですが、私はてっきり数値エラーでクラッシュするか、またはエラーメッセージが出るとばかり思っていました。しかし実際にはエラーは報告されず、ただ黙って隠面消去しない絵が表示されていたのでした。
I thought some messages should be reported when arithmetic errors happened. However, no errors reported and simply wrong picture was drawn.

GL_PROJECTIONにおける透視投影の設定にはgluPerspective()を使っていたのですが、これだけうまく動かないのです。glOrtho()やglFrustum()を使うとうまくいくのです。なのでgluPerspective()の設定だとばかり思っていました。確かにそうだったのですが、エラーが出ないので前方クリッピングの問題ではないと思い込んでいたのでした。ちなみに、glOrtho()やglFrustum()ではうまく行った理由は次の通りです。
- glOrtho() 直投影には前方クリッピング面は関係ありません
- glFrustum() 前方クリッピング面を慎重に調整しないとまともに絵が出ません。
それに引き替え、gluPerspective()は直観的で、すぐに絵が出るので、間違いに気づきにくかったのです。
OpenGL provide three ways to set camera. I used gluPerspective() which didn't work and other two worked fine because:
- glOrtho() near clipping surface doesn't mean enything
- glFrustum() I should adjust near clipping surface very carefully, otherwise no picture.

まあ、とにかく、
gluPerspective(3, 1.0, 0.0, 100.0)  絵は出るが隠面消去が間違っている
gluPerspective(3, 1.0, 1.0, 100.0) 正しい
ということがわかったのでした。
Anyway,
gluPerspective(3, 1.0, 0.0, 100.0) = wrong picrure diaplayed silently
gluPerspective(3, 1.0, 1.0, 100.0) = right picture

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

def Draw():
glEnable(GL_DEPTH_TEST)

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()

#glOrtho(-1.0, 1.0, -1.0, 1.0, -0.5, 1000.0) #good
#glFrustum(-1.0, 1.0, -1.0, 1.0, 40.0, 200.0) #good
#gluPerspective(3, 1.0, 0.0, 100.0) #bad hidden surface
gluPerspective(3, 1.0, 1.0, 100.0)

glMatrixMode(GL_MODELVIEW)
gluLookAt(30.0, 30.0, 0.0, 0.0, 0.0, -30.0, 0.0, 1.0, 0.0)

glBegin(GL_TRIANGLES)
glColor3f(0.0, 0.0, 1.0)
glVertex3f( 0.9, -0.9, -30.0)
glVertex3f( 0.9, 0.9, -30.0)
glVertex3f(-0.9, 0.0, -30.0)
glEnd()

glTranslatef(0.0, 0.0, -30.0)
glColor3f(0.5, 0.5, 0.5)
glutSolidSphere(0.7, 10, 10)

glColor3f(0.8, 0.5, 0.5)
glutSolidCube(1.0)
glFlush();

glutInit(sys.argv)
glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_SINGLE)
glutInitWindowSize(300, 300)
glutCreateWindow("Depth Test")
glutDisplayFunc(Draw)
glutMainLoop()



2008/05/09

Robot2 - simple humanoid

11軸の単純な人間型ロボットを作ってみました。胴体を中心とした全体回転1軸、頭2軸、手足各2軸x4本です。これも"OpenGL入門" エドワード・エンジェル著、滝沢徹・牧野祐子訳、ピアソン・エデュケーション刊を参考にして作ってみました。軸は多くなりましたが、glPush/PopMatrixが追加されただけで、原理は昨日のサンプルと全く同じです。Pythonはインタープリタでありデバッグが簡単なので、これくらいのコードなら数時間で動かせます。とても手軽です。キーボードで操作できます。大文字は逆方向に動きます。
e/E r/R (頭)
a/A s/S d/D f/F g/G (腕、胴体)
z/Z x/X c/C v/V (足)

Simple humanoid type robot with 11 moving axis, 1 for entire body, 2 for head, 2 for each leg, by refering a book "OpenGL, A PRIMER" by Edward Angel. glPush/PopMatrix is added to handle this complex structure. Python is pretty powerfull to debug my source code because it's an interpreter. You can operate the humanoid by using keyboard.

e/E r/R (head)
a/A s/S d/D f/F g/G (arms, entire body)
z/Z x/X c/C v/V (legs)


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

TORSO_RADIUS=0.1
TORSO_HEIGHT=0.4

UPPER_ARM_HEIGHT=0.2
UPPER_ARM_WIDTH=0.07

LOWER_ARM_HEIGHT=0.2
LOWER_ARM_WIDTH=0.05

UPPER_LEG_HEIGHT=0.2
UPPER_LEG_WIDTH=0.08

LOWER_LEG_HEIGHT=0.2
LOWER_LEG_WIDTH=0.06

SHOLDER_WIDTH = 0.2
HIP_WIDTH = 0.2

HEADX=0.1
HEADY=TORSO_HEIGHT

LUAX=-1.0 * SHOLDER_WIDTH / 2
RUAX=SHOLDER_WIDTH / 2
LUAY=RUAY=TORSO_HEIGHT
LLAY=RLAY=LOWER_ARM_HEIGHT

LULX=-1.0 * HIP_WIDTH / 2
RULX=HIP_WIDTH / 2
LULY=RULY=0
LLLY=RLLY=LOWER_LEG_HEIGHT

t0=0.0
t1=0.0
t2=0.0
t3=90.0
t4=0.0
t5=90.0
t6=0.0
t7=180.0
t8=0.0
t9=180.0
t10=0.0

def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)

#TORSO
glLoadIdentity()
gluLookAt(2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
gray()
glRotatef(t0, 0.0, 1.0, 0.0)
torso()
glPushMatrix()

#HEAD
cyan()
glTranslatef(0.0, HEADX, 0.0)
glRotatef(t1, 1.0, 0.0, 0.0)
glRotatef(t2, 0.0, 1.0, 0.0)
glTranslatef(0.0, HEADY, 0.0)
head()

#LEFT UPPER ARM
glPopMatrix()
glPushMatrix()
red()
glTranslatef(LUAX, LUAY, 0.0)
glRotatef(t3, 1.0, 0.0, 0.0)
upper_arm()

#LEFT LOWER ARM
green()
glTranslatef(0.0, LLAY, 0.0)
glRotatef(t4, 1.0, 0.0, 0.0)
lower_arm()

#RIGHT UPPER ARM
glPopMatrix()
glPushMatrix()
blue()
glTranslatef(RUAX, RUAY, 0.0)
glRotatef(t5, 1.0, 0.0, 0.0)
upper_arm()

#RIGHT UPPER ARM
cyan()
glTranslatef(0.0, RLAY, 0.0)
glRotatef(t6, 1.0, 0.0, 0.0)
lower_arm()

#LEFT UPPER LEG
glPopMatrix()
glPushMatrix()
magenta()
glTranslatef(LULX, LULY, 0.0)
glRotatef(t7, 1.0, 0.0, 0.0)
upper_leg()

#LEFT LOWER LEG
yellow()
glTranslatef(0.0, LLLY, 0.0)
glRotatef(t8, 1.0, 0.0, 0.0)
lower_leg()

#RIGHT UPPER LEG
glPopMatrix()
glPushMatrix()
pink()
glTranslatef(RULX, RULY, 0.0)
glRotatef(t9, 1.0, 0.0, 0.0)
upper_leg()

#RIGHT LOWER LEG
gray()
glTranslatef(0.0, RLLY, 0.0)
glRotatef(t10, 1.0, 0.0, 0.0)
lower_leg()

glPopMatrix()
glFlush()

def torso():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
gluCylinder(p, TORSO_RADIUS, TORSO_RADIUS, TORSO_HEIGHT, 10, 10)
glPopMatrix()

def head():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
glutWireSphere(0.1, 10, 10)
glPopMatrix()

def upper_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(UPPER_ARM_WIDTH, UPPER_ARM_HEIGHT, UPPER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(LOWER_ARM_WIDTH, LOWER_ARM_HEIGHT, LOWER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def upper_leg():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(UPPER_LEG_WIDTH, UPPER_LEG_HEIGHT, UPPER_LEG_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_leg():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(LOWER_LEG_WIDTH, LOWER_LEG_HEIGHT, LOWER_LEG_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def red():
glColor3f(1.0, 0.0, 0.0)
def green():
glColor3f(0.0, 1.0, 0.0)
def blue():
glColor3f(0.0, 0.0, 1.0)
def cyan():
glColor3f(0.0, 1.0, 1.0)
def magenta():
glColor3f(1.0, 1.0, 0.0)
def yellow():
glColor3f(1.0, 0.0, 1.0)
def pink():
glColor3f(1.0, 0.5, 0.5)
def gray():
glColor3f(0.7, 0.7, 0.7)

def mykey(key, x, y):
global t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
if key=='d': # TORSO
t0 = t0 + 10.0
elif key=='D':
t0 = t0 - 10.0
elif key=='e': # HEAD 1
t1 = t1 + 10.0
elif key=='E':
t1 = t1 - 10.0
elif key=='r': # HEAD 2
t2 = t2 + 10.0
elif key=='R':
t2 = t2 - 10.0
elif key=='s': # LUA
t3 = t3 + 10.0
elif key=='S':
t3 = t3 - 10.0
elif key=='a': # LLA
t4 = t4 + 10.0
elif key=='A':
t4 = t4 - 10.0
elif key=='f': # RUA
t5 = t5 + 10.0
elif key=='F':
t5 = t5 - 10.0
elif key=='g': # RLA
t6 = t6 + 10.0
elif key=='G':
t6 = t6 -10.0
elif key=='x': # LUL
t7 = t7 + 10.0
elif key=='X':
t7 = t7 - 10.0
elif key=='z': # LLL
t8 = t8 + 10.0
elif key=='Z':
t8 = t8 -10.0
elif key=='c': # RUL
t9 = t9 + 10.0
elif key=='C':
t9 = t9 - 10.0
elif key=='v': # RLL
t10 = t10 + 10.0
elif key=='V':
t10 = t10 - 10.0
elif key=='q':
sys.exit()

print "params: ", t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
glutPostRedisplay()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'robot' )
glutDisplayFunc( display )
glutKeyboardFunc(mykey)
p=gluNewQuadric()
gluQuadricDrawStyle(p, GLU_LINE)

glClearColor(0.0, 0.0, 0.0, 0.0)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(30, 1.0, 0.0, 100.0)

glutMainLoop()

2008/05/08

Robot - simple hierarchical model

移動(Translate)や回転(Rotate)は拡大・縮小はインクリメンタルであるため、リセットしなければ変換用の行列に加算や乗算がどんどん加えられて行きます。土台、下腕、上腕を持つ簡単なロボットの例を、"OpenGL入門" エドワード・エンジェル著、滝沢徹・牧野祐子訳、ピアソン・エデュケーション刊を参考にして作ってみました。Cで書かれた部分的なサンプルコードをPythonに書き直すのは至って簡単でした。Translateの基本概念が前回分かったので、うんと楽です。ただ、変換の順番が問題になると、まだよく分かっていないことが露呈します。

Instance transformation is incremental. This is good for hierarchical instances with dependency on other objects in parent hierarchy. By refering a book "OpenGL, A PRIMER" by Edward Angel, I made a simple robot example using Python. "base" instance has a child instance "lower_arm" and "lower_arm" has a child instance "upper_arm". Translating C sample code to Python was very easy because I learned how the MODELVIEW matrix works.

In the following example, use key 'q', 'a', 'w', 's', 'e', 'd' to move each part of the robot.


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

BASE_RADIUS = 0.1
BASE_HEIGHT = 0.1
UPPER_ARM_WIDTH = 0.05
UPPER_ARM_HEIGHT=0.5
LOWER_ARM_WIDTH=0.08
LOWER_ARM_HEIGHT=0.3
tb=0
tl=45
tu=30

def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)

glLoadIdentity()
gluLookAt(2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
glColor3f(1.0, 0.0, 0.0)
glRotatef(tb, 0.0, 1.0, 0.0)
base()
glTranslatef(0.0, BASE_HEIGHT, 0.0)
glColor3f(0.0, 1.0, 0.0)
glRotatef(tl, 0.0, 0.0, 1.0)
lower_arm()
glTranslatef(0.0, LOWER_ARM_HEIGHT, 0.0)
glRotatef(tu, 0.0, 0.0, 1.0)
glColor3f(0.0, 0.0, 1.0)
upper_arm()
glFlush()

def base():
glPushMatrix()
glRotatef(-90.0, 1.0, 0.0, 0.0)
gluCylinder(p, BASE_RADIUS, BASE_RADIUS, BASE_HEIGHT, 10, 10)
glPopMatrix()

def upper_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0)
glScalef(UPPER_ARM_WIDTH, UPPER_ARM_HEIGHT, UPPER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def lower_arm():
glPushMatrix()
glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0)
glScalef(LOWER_ARM_WIDTH, LOWER_ARM_HEIGHT, LOWER_ARM_WIDTH)
glutSolidCube(1.0)
glPopMatrix()

def mykey(key, x, y):
global tb, tl, tu
if key=='q': # +base
tb = tb + 5.0
elif key=='a': # -base
tb = tb - 5.0
elif key=='w': #+lower
tl = tl + 5.0
elif key=='s': #-lower
tl = tl - 5.0
elif key=='e': #+upper
tu = tu + 5.0
elif key=='d': #-upper
tu = tu - 5.0
print "tb=", tb, " tl=", tl, " tu=", tu
glutPostRedisplay()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'robot' )
glutDisplayFunc( display )
glutKeyboardFunc(mykey)
p=gluNewQuadric()
gluQuadricDrawStyle(p, GLU_LINE)

glClearColor(0.0, 0.0, 0.0, 0.0)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(30, 1.0, 0.0, 100.0)

glutMainLoop()

real meening of gluLookAt

この2日ほどカメラ座標の移動に取り組んでいたのですが、やっと分かりました。OpenGLはあくまでもMODELVIEW行列を設定してから何か絵を描くと、MODELVIEW行列で指定した位置、方向、大きさでモデルが描画されるというだけのAPIなのです。これが、オブジェクト指向や、データベースや、オブジェクトの絶対座標とカメラの移動などに慣れている人には大きな関門になるのです。本をよく読むと、次のようなことが書いてはあるのですが、なかなか理解できませんでした。
・カメラは動かない。常に図形側を全部動かす。
・gluLookAt関数はカメラの移動であるが、実際にはカメラの移動の逆行列をセットしているだけ。
・glLoadIdentity()を実行するとgluLookAtでセットされた行列もリセットされる。カメラは元の位置に戻る。
・glLoadIdentity()実行後に元のカメラ位置で描画するには、再度gluLookAtを実行する。
・データベースはなく、画面に書いているだけである。再描画をすると全部書き直すだけである。
・データベース構築はアプリケーションの責任

I spent whole two two days to understand gluLookAt() command. This just set inversed matrix of MODELVIEW. This simulate camera movement, but it is not a real camera movement. This is very difficult to understand for person who is using Object oriented, database, camera movement with world coordinate system. There are no actual camera movement, no world coordinate system, no database in OpenGL API. It just draw pictures on screen using fixed camera and MODELVIEW matrix. Books said as follows which was really difficult for me to understand.
- Camera position is fixed. We always move all drawing objects which is MODELVIEW.
- gluLookAt simulate camera movement. But, it just set inverse matrix of MODELVIEW.
- glLoadIdentity() reset matrix set by gluLookAt(). glLoadIdentity() resets camera movement.
- After glLoadIdentity(), use the same gluLookAt() to draw something from the camera positon.
- OpenGL doesn't handle database onject. It just draw something on screen.
- Database sould be done by application outside OpenGL.

このサンプルでは、キーボードを押して、異なる図形に異なるカメラアングルを与えることができます。
The following example moves two different cameras for different group of objects.

Please push the following keys to see what happens.
h
j
k
u
m
i
n
a
d
w
x
e
z
v
b


#movey.py

import sys
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
xx=yy=zz=x1=y1=z1=0.0
fov=30.0

def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)

glLoadIdentity()
gluLookAt(20.0, 20.0, 20.0, x1, y1, z1, 0.0, 1.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glutWireCone(1.0,1.0,12,12)
glTranslatef(0.0, 0.0, -5.0)
glutWireCone(1.0,1.0,12,12)
glTranslatef(0.0, 0.0, -5.0)
glutWireCone(1.0,1.0,12,12)

glLoadIdentity()
gluLookAt(20.0, 20.0, 20.0, xx, yy, zz, 0.0, 1.0, 0.0)
glColor3f(1.0, 1.0, 0.0)
glTranslatef(0.0, 0.0, 5.0)
glutWireCone(1.0,1.0,12,12)

glLoadIdentity()
gluLookAt(20.0, 20.0, 20.0, xx, yy, zz, 0.0, 1.0, 0.0)
glColor3f(0.0, 1.0, 1.0)
glTranslatef(0.0, 0.0, 8.0)
glRotatef(-90, 1.0, 0.0, 0.0)
glutWireCone(1.0,1.0,12,12)

glFlush()

def mykey(key, x, y):
global xx, yy, zz, x1, y1, z1, fov
if key=='k': # +x
xx = xx + 1.0
print "xx=", xx
elif key=='h': # -x
xx = xx - 1.0
print "xx=", xx
elif key=='u': #+y
yy = yy + 1.0
print "yy=", yy
elif key=='m': #-y
yy = yy - 1.0
print "yy=", yy
elif key=='i': #-z
zz = zz - 1.0
print "zz=", zz
elif key=='n': #+z
zz = zz + 1.0
print "zz=", zz

elif key=='d': # +x1
x1 = x1 + 1.0
print "x1=", x1
elif key=='a': # -x1
x1 = x1 - 1.0
print "x1=", x1
elif key=='w': #+y1
y1 = y1 + 1.0
print "y1=", y1
elif key=='x': #-y1
y1 = y1 - 1.0
print "y1=", y1
elif key=='e': #-z1
z1 = z1 - 1.0
print "z1=", z1
elif key=='z': #+z1
z1 = z1 + 1.0
print "z1=", z1

elif key=='j': # center camera to origin 0,0
xx=yy=zz=x1=y1=z1=0.0
fov=30
print "reset x,y,z to (0,0)"
elif key=='v': # +fov
fov = fov + 5
elif key=='b': #-fov
fov = fov - 5

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(fov, 1.0, 0.0, 100.0)

glutPostRedisplay()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'model' )
glutDisplayFunc( display )
glutKeyboardFunc(mykey)

glClearColor(0.0, 0.0, 0.0, 0.0)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(30, 1.0, 0.0, 100.0)

glutMainLoop()

2008/05/06

gluLookAt by keyboard

gluLookAt()関数を使って、キーボードを使ってカメラをパンできるようになりました。使用するキーは次の通り。
# u(+y) i(-z)
# h(-x) j(reset) k(+x)
# n(+z) m(-y)
レンズの光軸の指す点を3次元座標で指定するのですが、たとえばキー"k"を押すと、その座標が+1されて、カメラがちょっと右向きます。すると景色は左に動きます。

I can pan the camera by using gluLookAt() command and keyboard. Push a key, u, i, h,j,k,n, or m, camera will pan accordingly.
# u(+y) i(-z)
# h(-x) j(reset) k(+x)
# n(+z) m(-y)
For example, push 'k' key once, x=x+1. You pan right your camera to a point x+1, y, z. Thus, objects you are seeing will move to left.


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

# draw x, y, z axis with index
# draw 3 sphere
# use keyboard to pan camera from a position (20,20,20)
#
# u(+y) i(-z)
# h(-x) j(reset) k(+x)
# n(+z) m(-y)

def display():
glClear(GL_COLOR_BUFFER_BIT)
axis()
glColor3f(0.0, 1.0, 0.0)
mySphere = gluNewQuadric()
gluQuadricDrawStyle(mySphere, GLU_LINE)

glTranslatef(0.0, 0.0, -5.0)
gluSphere(mySphere, 2.0, 12, 12)

glTranslatef(0.0, 0.0, 10.0)
gluSphere(mySphere, 1.0, 12, 12)

glTranslatef(0.0, -5.0, 0.0)
glRotatef(90.0, 0.0, 1.0, 0.0)
gluSphere(mySphere, 1.0, 12, 12)

glFlush()

def axis():
glClear( GL_COLOR_BUFFER_BIT )

glBegin(GL_LINES)
glColor3f(0.3, 0.3, 0.3)
glVertex3f(-10.0, 0.0, 0.0)
glVertex3f(10.0, 0.0, 0.0)
glVertex3f(0.0, -10.0, 0.0)
glVertex3f(0.0, 10.0, 0.0)
glVertex3f(0.0, 0.0, -10.0)
glVertex3f(0.0, 0.0, 10.0)
glEnd()

biggest = 10
x = 88
y = 89
z = 90
r = range (0,(biggest+1))
for i in r:
ascii = 48+i

# x axis positive
glRasterPos3f(i, 0.0, 0.0)
glColor3f(0.3, 0.3, 0.3)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, x )
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)
# x axis negative
glRasterPos3f(-i, 0.0, 0.0)
glColor3f(0.5, 0.0, 0.0)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, x)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)

# y axis positive
glRasterPos3f(0.0, i, 0.0)
glColor3f(0.3, 0.3, 0.3)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, y)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)
# x axis negative
glRasterPos3f(0.0, -i, 0.0)
glColor3f(0.5, 0.0, 0.0)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, y)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)

# z axis positive
glRasterPos3f(0.0, 0.0, i)
glColor3f(0.3, 0.3, 0.3)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, z)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)
# z axis negative
glRasterPos3f(0.0, 0.0, -i)
glColor3f(0.5, 0.0, 0.0)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, z)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)

glFlush()

def init():
glClearColor(0.0, 0.0, 0.0, 0.0)
glColor3f(0.0, 0.0, 0.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(30, 1.0, 0.0, 100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(20.0, 20.0, 20.0, xx, yy, zz, 0.0, 1.0, 0.0)

def mymouse(but, stat, x, y):
if stat == GLUT_DOWN:
if but == GLUT_LEFT_BUTTON:
print x, y, "Press right button to exit"
else:
sys.exit()

def myidle():
glutPostRedisplay()

def mykey(key, x, y):
global xx, yy, zz
if key=='k': # +x
xx = xx + 1.0
print "xx=", xx
elif key=='h': # -x
xx = xx - 1.0
print "xx=", xx
elif key=='u': #+y
yy = yy + 1.0
print "yy=", yy
elif key=='m': #-y
yy = yy - 1.0
print "yy=", yy
elif key=='i': #-z
zz = zz - 1.0
print "zz=", zz
elif key=='n': #+z
zz = zz + 1.0
print "zz=", zz
elif key=='j': # center camera to origin 0,0
xx=yy=zz=0.0
print "reset x,y,z to (0,0)"

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(20.0, 20.0, 20.0, xx, yy, zz, 0.0, 1.0, 0.0)
glutPostRedisplay()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'model' )
glutDisplayFunc( display )
glutMouseFunc(mymouse)
glutKeyboardFunc(mykey)
xx=yy=zz=0.0
init()
glutMainLoop()

2008/05/05

Writing x, y and z axis

やっと少し3次元らしくなってきました。画面真っ黒からなかなか抜け出せなかったので、まずは、x, y, z軸を書いて目盛を文字で書いてみました。効果てきめんで、自分のカメラがどこを向いているのかやっと分かりました。
Some 3D graphics are one screen. I worked on black window for some time and then realized writing x/y/z axis with scale should help. This gives me a good idea which direction I'm lookign at.

ただし、glRasterPos3f()という関数のx,y,z軸の方向が逆を向いているようなのです。プラス方向とマイナス方向が逆みたいです。たとえば、
glRasterPos3f(1.0, 1.0, 1.0)

glTranslatef(-1.0, -1.0, -1.0)
と同じような意味を持っているようなのです。
glRasterPos3f() looks a little strange. Plus and minus directions are opposite on all axis.

まあ、とにかく画面真っ黒から座標軸が見えるようになったのは大きな進歩でして、少し先に進められそうです。OpenGLはステートマシン言語ですが、オブジェクト指向言語に慣れていると、とても分かりにくいですね。とにかくMODELVIEW行列(4x4)をセットして、何かオブジェクトを置くと、MODELVIEW行列でセットされた位置、向き、縮尺でオブジェクトが出てくるという感じです。
Anyway, now I can see x/y/z axis. OpenGL is a state machine language, not object oriented. I shoould set MODELVIEW matrix before creating an object.

今日のところのソースコードは次の通り。
Python source code as of today is as follows.


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

def display():
glColor3f(0.0, 1.0, 0.0)
mySphere = gluNewQuadric()
gluQuadricDrawStyle(mySphere, GLU_LINE)

glTranslatef(0.0, 0.0, -5.0)
gluSphere(mySphere, 2.0, 12, 12)

glTranslatef(0.0, 0.0, 10.0)
gluSphere(mySphere, 1.0, 12, 12)

glTranslatef(0.0, -5.0, 0.0)
glRotatef(90.0, 0.0, 1.0, 0.0)
gluSphere(mySphere, 1.0, 12, 12)

glFlush()

def axis():
glClear( GL_COLOR_BUFFER_BIT )

glBegin(GL_LINES)
glColor3f(0.3, 0.3, 0.3)
glVertex3f(-10.0, 0.0, 0.0)
glVertex3f(10.0, 0.0, 0.0)
glVertex3f(0.0, -10.0, 0.0)
glVertex3f(0.0, 10.0, 0.0)
glVertex3f(0.0, 0.0, -10.0)
glVertex3f(0.0, 0.0, 10.0)
glEnd()

biggest = 10
x = 88
y = 89
z = 90
r = range (0,(biggest+1))
for i in r:
ascii = 48+i

# x axis positive
glRasterPos3f(i, 0.0, 0.0)
glColor3f(0.3, 0.3, 0.3)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, x )
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)
# x axis negative
glRasterPos3f(-i, 0.0, 0.0)
glColor3f(0.5, 0.0, 0.0)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, x)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)

# y axis positive
glRasterPos3f(0.0, i, 0.0)
glColor3f(0.3, 0.3, 0.3)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, y)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)
# x axis negative
glRasterPos3f(0.0, -i, 0.0)
glColor3f(0.5, 0.0, 0.0)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, y)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)

# z axis positive
glRasterPos3f(0.0, 0.0, i)
glColor3f(0.3, 0.3, 0.3)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, z)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)
# z axis negative
glRasterPos3f(0.0, 0.0, -i)
glColor3f(0.5, 0.0, 0.0)
if i==biggest:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, z)
else:
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, ascii)

glFlush()

def init():
glClearColor(0.0, 0.0, 0.0, 0.0)
glColor3f(0.0, 0.0, 0.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(30, 1.0, 0.0, 100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(20.0, 20.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
axis()
def mymouse(but, stat, x, y):
if stat == GLUT_DOWN:
if but == GLUT_LEFT_BUTTON:
print x, y, "Press right button to exit"
else:
sys.exit()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'model' )
glutDisplayFunc( display )
glutMouseFunc(mymouse)

init()
glutMainLoop()

2008/05/04

glutCreateMenu

glutCreateMenuを実行するとエラーになるのですが、その意味が分かりません。OpenGLが標準でサポートするのは通常マウスの右ボタンに設定するポップアップメニューだけなのですが、これの使い方がわからないのです。本やWebで見つかるにCのサンプルがPythonでは動きません。当面メニュー無しでやります。解決策をご存じの方はぜひ教えて下さい。

glutCreateMenu doesn't work on my Python environment using some example codes I can find in bookes and web written in C language. I should continue larning OpenGL without using popup menus. If you have any suggestion, please let me know.

###Error Message
Traceback (most recent call last):
File "C:/Python25/menu.py", line 28, in
glutCreateMenu(mymenu)
File "C:\Python25\Lib\site-packages\OpenGL\GLUT\special.py", line 169, in glutCreateMenu
result = simple.glutCreateMenu( cCallback )
ArgumentError: argument 1: : expected WinFunctionType instance instead of WinFunctionType

### Sourece cose

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

def display():
glClear( GL_COLOR_BUFFER_BIT )
glBegin(GL_POLYGON)
glVertex2f(-0.5, -0.5)
glVertex2f(-0.5, 0.5)
glVertex2f(0.5, 0.5)
glVertex2f(0.5, -0.5)
glEnd()

def mymenu(value):
if value == 1:
glClear(GL_COLOR_BUFFERF_BIT)
if value == 2:
sys.exit()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'simple' )
glutDisplayFunc( display )

glutCreateMenu(mymenu)
glutAddMenuEntry("clear",1)
glutAddMenuEntry("exit",2)
glutAttachMenu(GLUT_RIGHT_BUTTON)

glutMainLoop()

glutReshapeFunc

glutReshapeFuncは、実行途中にウインドウの大きさが変更された時に起動されるコールバックを定義できるのですが、これを行うとPythonがクラッシュしてしまいます。昨日からいろいろと試したのですが、どうにもなりません。当面はウィンドウのリサイズは禁止ということで進めたいと思います。もし解決策をご存じでしたら教えて下さい。

If I define any callback routine to "glutReshapeFunc" which I can define a routine to be invoked after graphic window size is changed by user. Unfortunately, this crashes both Python shell and IDLE. I tried to fix this by changing my code in several different ways, but I couldn't. If you have any suggestion to solve this problem, please let me know.

Sample code which cause crash:

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

def display():
glClear( GL_COLOR_BUFFER_BIT )
glBegin(GL_POLYGON)
glColor3f(1.0, 1.0, 1.0)
glVertex2f(-0.5, -0.5)
glVertex2f(-0.5, 0.5)
glVertex2f(0.5, 0.5)
glVertex2f(0.5, -0.5)
glEnd()
glColor3f(1.0, 0.0, 0.0)
glRasterPos2f(0.3, 0.3)
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, 65)
glFlush()

def reshape(w, h):
print "reshape detected"
glutPostRedisplay()

glutInit( sys.argv )
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB )
glutInitWindowSize( 500, 500 )
glutInitWindowPosition(0,0)
glutCreateWindow( 'simple' )
glutDisplayFunc( display )
glutReshapeFunc( reshape )
glutMainLoop()

2008/05/02

Tkinter

Tcl/TkのTkをPythonから使うようにするのがTkinterです。デモを見るとTcl/Tkと同じことができそうなのですが、ソースを見てみると、何となくいやな予感が。
一番簡単なサンプルだとこんな感じなのですが、最後の文が無限ループなので、これよりプログラムを先に進めることができません。

from Tkinter import *
root = Tk()
w=Label(root, text="Hello, world!")
w.pack()
root.mainloop()

Tkを抜けた時点でOpenGLの
glutMainLoop()
が走り出すので、ここを何とかしないといけないですね。初心者のレベルを超えた難題ですので、とりあえずTkinterは棚上げにしたいと思います。

Both Tkinter and OpenGL have main loop. Because those are infinite loop, I should exit the first loop before starting the second loop. This is a very difficult problem I shouldn't spend time at this point.

glScale


OpenGLの提供する3番目の標準変換は拡大縮小(スケーリング)です。この例では前の例のglRotateをglScaleに置き換えただけです。ただし、回転と違って拡大縮小は連続して同じ操作を行うと画面からはみ出してしまうか、または点になってしまいます。そこで、拡大を10回行ったら縮小を10回行うように制御してみました。Pythonのif文による制御はC言語に似ているので簡単です。{}を:とインデントに置き換えて、;を取り除けばOKです。グローバル変数は自由に参照できますが、グローバル変数の値を変更するにはglobal宣言が必要です。

I just replaced glRotatef to glScale to illustrate scaling translation of OpenGL. Not like glRotate, I cannot continue scale down becuase the triangle will be too small. I should control sclae up and down using control statement in Python. Python provedes "if" statement very similar to C.


sc = up = 0
def timer(value):
global sc, up
if up:
glScale(1.1 , 1.1 , 1)
sc += 1
if sc > 0:
up = 0
else:
glScale(0.9091 , 0.9091 , 1)
sc -= 1
if sc < -8:
up = 1
glutPostRedisplay()
glutTimerFunc(50 , timer , 0)