2008/06/09

The result of 360 degree cubic panorama player looks like this. Type keys to rotate/zoom the panorama image.
"s" home position
"a" rotate left
"d" rotate right
"w" rotate up
"x" rotate down
"-" zoom in
"+" zoom out

実行するとこんな感じで表示されます。キーボードを使って回転させます。

I should do the following thins to fix issues or enhance usability. 次のような点を改善しなければなりません。
1. Rotate -90 degree of the initial image
2. Use mouse rather than keyboard
3. Fix bugs of rotation
4. Limit the FOV not to show black portion

QTVR functionality usign OpenGL (1)

To display 360 degree cubic panorama movie, either QuickTime(QTVR) or Adobe Flash player is used as viewer. I like QTVR very much and I participate "World Wide Panorama" ten times, http://geoimages.berkeley.edu/worldwidepanorama/wwppeople/html/RikuichiKishimoto.html
360度キュービックVRの表示には通常QuickTime(QTVR)またはAdobe Flash Playerが使われます。私はQTVRの製作が趣味でして、World Wide Panoramaにも参加しています。

It would be very easy to implement 360 degree cubic panorama viewer using OpenGL. The source image has 6 cubic faces in one JPEG file.
OpenGLでキュービックパノラマを実現することは、割と簡単だと思われますので、やってみました。普通は次のような6面の画像を繋げたJPEGの画像を使いますが、簡単化のため6枚の別々の写真に分けました。
I manually cut the image into 6 pieces to simplify the OpenGL program.
Convert six JPEG files into six texture and attach to six cubic faces. It's very straight foward. The following code still have some bugs, but I can show you the basic idea. It's really a "inside rotating cube". まだバグなどがありますが、アイデアとしては次のようなものになります。


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

vertices = ((-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))
uuu = 0.1
fov = 120
xzt = yzt = 0
yyy = math.sin(yzt)
r = math.cos(yzt)
zzz = r * math.sin(xzt)
xxx = r * math.cos(xzt)

def polygon(a, b, c, d):
glBegin(GL_POLYGON)
glTexCoord2f(0.0, 0.0)
glVertex3fv(vertices[a])
glTexCoord2f(0.0, 1.0)
glVertex3fv(vertices[b])
glTexCoord2f(1.0, 1.0)
glVertex3fv(vertices[c])
glTexCoord2f(1.0, 0.0)
glVertex3fv(vertices[d])
glEnd()

def cube():
global texture
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, texture[4])
polygon(4,5,6,7)
glBindTexture(GL_TEXTURE_2D, texture[1])
polygon(1,5,4,0)
glBindTexture(GL_TEXTURE_2D, texture[2])
polygon(3,7,6,2)
glBindTexture(GL_TEXTURE_2D, texture[0])
polygon(0,4,7,3)
glBindTexture(GL_TEXTURE_2D, texture[5])
polygon(1,0,3,2)
glBindTexture(GL_TEXTURE_2D, texture[3])
polygon(2,6,5,1)

def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()

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

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# eye, at, up
gluLookAt(0.0, 0.0, 0.0, xxx,yyy,zzz, 0,1,0)

cube()
glFlush()

def mykey(key, x, y):
global xxx, yyy, zzz, fov, xzt, yzt
if key=='a': # -xzt
xzt = xzt - uuu
elif key=='d': # +xzt
xzt = xzt + uuu
elif key=='x': # -yzt
yzt = yzt - uuu
elif key=='w': # +yzt
yzt = yzt + uuu
elif key=='s': # default
xzt = yzt = 0.0

elif key=='-': # zoom out
fov = fov - 10
elif key=='+': # default
fov = fov + 10

yyy = math.sin(yzt)
rrr = math.cos(yzt)
zzz = rrr * math.sin(xzt)
xxx = rrr * math.cos(xzt)

print "xyzf:", xxx, yyy, zzz, fov
glutPostRedisplay()

def gentex(t, img):
# use the given texture
glBindTexture(GL_TEXTURE_2D, t)
print "texture", t

# read texture data from .bmp file using Image library
image=Image.open(img)
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())

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

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

glEnable(GL_DEPTH_TEST) #hidden-surface 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 two texture
# different format from C, void glGenTexture(n, *name)
texture=glGenTextures(6)

gentex(texture[0], "./pcube/r1.bmp")
gentex(texture[1], "./pcube/r2.bmp")
gentex(texture[2], "./pcube/r3.bmp")
gentex(texture[3], "./pcube/r4.bmp")
gentex(texture[4], "./pcube/rb.bmp")
gentex(texture[5], "./pcube/rt.bmp")

glutMainLoop()