TOP   Ruby

書籍『図解 OpenGLによる3次元CGアニメーション』のOpalサンプル


書籍内のサンプルコードをOpalで書き直したものです。書籍のコードと比べ、かなりシンプルに書けていると思います。

コード最上部の require './opal' は以下すべて省略してあります。実際にはこれを書き加えて下さい。

ロボットアーム

p.42. キーでアームの角度を変えることができます。

p3-robot.rb

gl = nil
shoulder = elbow = 0

display = proc do
  gl.clr_matrix
  glTranslated(-2, 0, -5)
  
  gl.block do
    gl.rotate(shoulder)
    glTranslated(1, 0, 0)
    gl.block do
      glScaled(2, 0.4, 1)
      glutWireCube(1)
    end
    glTranslated(1, 0, 0)
    gl.rotate(elbow)
    glTranslated(1, 0, 0)
    gl.block do
      glScaled(2, 0.4, 1)
      glutWireCube(1)
    end
  end
end

keyboard = proc do |key|
  case key
  when "s"
    shoulder = (shoulder + 5) % 360
  when "S"
    shoulder = (shoulder - 5) % 360
  when "e"
    elbow = (elbow + 5) % 360
  when "E"
    elbow = (elbow - 5) % 360
  when " "
    exit
  end
  gl.redraw
end

Opal.app width: 500, height: 500, buffering: GLUT_DOUBLE do |g|
  gl = g
  g.rotate = [0, 0, 1]
  
  g.display = display
  g.reshape = proc {gluPerspective(60, 500 / 500, 0.1, 20)}
  g.keyboard = keyboard
end

太陽系

p.46. キーで惑星を、公転・自転させることができます。

p3-planet.rb

gl = nil
year = day = 0

display = proc do
  gl.clr_matrix
  gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0)
  gl.color(1, 1, 1)
  
  gl.block do
    glutWireSphere(1, 20, 16)    #半径1、緯度方向の分割数20, 経度方向の分割数16の球 「太陽」
    gl.rotate(year)
    glTranslated(2, 0, 0)
    gl.rotate(day)
    glutWireSphere(0.2, 10, 8)   #「惑星」
  end
end

keyboard = proc do |key|
  case key
  when "d"
    day = (day + 10) % 360
  when "D"
    day = (day - 10) % 360
  when "y"
    year = (year + 5) % 360
  when "Y"
    year = (year - 5) % 360
  when " "
    exit
  end
  gl.redraw
end

Opal.app width: 500, height: 500, buffering: GLUT_DOUBLE do |g|
  gl = g
  g.rotate = [0, 1, 0]
  
  g.display = display
  g.reshape = proc {gluPerspective(60, 500 / 500, 0.1, 20)}
  g.keyboard = keyboard
end

p.57. 太陽系アニメーション。

p4-MovingPlanet.rb

gl = nil
year = day = 0
sampling_time = 50

display = proc do
  gl.clr_matrix
  gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0)
  gl.color(1, 1, 1)
  
  gl.block do
    glutWireSphere(1, 20, 16)
    gl.rotate(year)
    glTranslated(2, 0, 0)
    gl.rotate(day)
    glutWireSphere(0.4, 10, 8)
  end
end

Opal.app width: 500, height: 500, buffering: GLUT_DOUBLE do |g|
  gl = g
  g.perspective
  g.rotate = [0, 1, 0]
  
  g.display = display
  g.repeat(sampling_time) do
    year = (year + 1) % 360
    day = (day + 5) % 360
    g.redraw
  end
  g.keyboard = proc {|key| exit if key == " "}
end

ティーポット

p.52. ティーポットが飛んできます。じつはティーポットは移動しておらず、視点が近づいている。

p4-FlyingTeaPot.rb

gl = nil
theta = 0
dist = -10

display = proc do
  gl.block do
    gl.look_to = [0, 1, dist + 1]
    gl.look_at(0, 1, dist)
    gl.xyzAxis(length: 10)    #座標軸の表示
    gl.block do
      glTranslated(1, 2, 0)
      gl.rotate(theta)
      gl.color(1, 0, 0)
      glutWireTeapot(1)
    end
    
    gl.color(1, 1, 1)    #床
    -35.step(35, 2) do |i|
      gl.line(i, 0, -35, i, 0, 35)
      gl.line(-50, 0, i, 50, 0, i)
    end
  end
end

Opal.app width: 500, height: 500, buffering: GLUT_DOUBLE do |g|
  gl = g
  g.perspective(90, 500 / 500, 0.1, 20)
  g.clr_matrix
  g.rotate = [1, 1, 0]
  
  g.display = display
  g.idle = proc do
    dist += 0.03
    g.idle = nil if dist >= -1
    theta -= 360 if (theta += 0.5) >= 360
    g.redraw
  end
  g.keyboard = proc {|key| exit if key == " "}
end

メニュー操作

p.66. 表示する倍率をメニューから選択します。画面下に倍率を表示します。

p5-menu.rb

gl = nil
size_of_teapot = 1

getValueFromMenu = proc do |value|
  size_of_teapot =
    case value
    when 1 then 0.5
    when 2 then 1
    when 3 then 2
    end
end

mySetMenu = proc do
  glutCreateMenu(getValueFromMenu)
  gl.add_menu("x 0.5", 1)
  gl.add_menu("x 1.0", 2)
  gl.add_menu("x 2.0", 3)
  glutAttachMenu(GLUT_RIGHT_BUTTON)    #マウスの右ボタンでメニュー表示
end

display = proc do
  gl.block do
    gl.clr_matrix
    glTranslated(0, 0, -3)
    gl.color(1, 0, 0)
    glutWireTeapot(size_of_teapot)
    
    gl.color(0, 1, 0)
    glRasterPos3d(-0.3, -1.5, 0)    #文字の位置
    str = "Size is " + sprintf("%4.1f", size_of_teapot)
    gl.print(str)    #文字の表示
  end
end

Opal.app width: 500, height: 500, buffering: GLUT_DOUBLE do |g|
  gl = g
  mySetMenu.call
  
  g.display = display
  g.reshape = proc {|w, h| gluPerspective(60, w / h.to_f, 0.1, 20)}
  g.idle = proc {g.redraw}
  g.keyboard = proc {|key| exit if key == " "}
end

照明とシェーディング

p6-Lights.rb

gl = nil

mySetLight = proc do
  l0_position = [ 1,  1, 1, 1]
  l1_position = [-1, -1, 1, 1]
  l1_ambient  = [0, 0, 0.5, 1]
  l1_diffuse  = [0, 0,   1, 1]
  l1_specular = [0, 0,   1, 1]
  
  gl.light(0, GL_POSITION, l0_position)
  gl.light(1, GL_POSITION, l1_position)
  gl.light(1, GL_AMBIENT,  l1_ambient)
  gl.light(1, GL_DIFFUSE,  l1_diffuse)
  gl.light(1, GL_SPECULAR, l1_specular)
  
  gl.light_enable(0)
  gl.light_enable(1)
end

display = proc do 
  mtrl_specular = [1, 1, 1, 1]
  mtrl_shininess = [50]
  
  glEnable(GL_DEPTH_TEST)
  glEnable(GL_LIGHTING)
  
  glMaterialfv(GL_FRONT, GL_SPECULAR, mtrl_specular)
  glMaterialfv(GL_FRONT, GL_SHININESS, mtrl_shininess)
  glutSolidSphere(1, 20, 16)
  glFlush
end

Opal.app width: 500, height: 500, depth: GLUT_DEPTH do |g|
  gl = g
  mySetLight.call
  glShadeModel(GL_SMOOTH)
  gluLookAt(0, 0, 3, 0, 0, 0, 0, 1, 0)
  
  g.display = display
  g.reshape = proc {|w, h| gluPerspective(60, w / h.to_f, 0.1, 20)}
  g.keyboard = proc {|key| exit if key == " "}
end

箱が回転します。内側と外側で色がちがいます。

p6-LightModel.rb

gl = nil
gPitch = 30
gYaw = 30

mySetLight = proc do
  l0_position = [1, 1, 1, 1]
  gl.light(0, GL_POSITION, l0_position)
  gl.light_enable
  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1)    #1はGL_TRUEの代わり
end

display = proc do
  gl.clr_matrix
  
  ambient  = [0.7 , 0.7 , 0.7 , 1]
  diffuse  = [0.75, 0.75, 0.75, 1]
  specular = [0.25, 0.25, 0.25, 1]
  insideface = [1, 0, 0, 1]
  
  glEnable(GL_DEPTH_TEST)
  glEnable(GL_LIGHTING)
  
  glMaterialfv(GL_FRONT, GL_AMBIENT, ambient)
  glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse)
  glMaterialfv(GL_FRONT, GL_SPECULAR, specular)
  glMaterialf(GL_FRONT, GL_SHININESS, 50)
  
  glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, insideface)
  glMaterialf(GL_BACK, GL_SHININESS, 60)
  
  gl.block do
    glRotated(gPitch, 1, 0, 0)
    glRotated(gYaw  , 0, 0, 1)
    
    gl.draw(GL_QUADS) do
      glNormal3d(0, 0, -1)
      gl.vtx3(-1, -1, -1)
      gl.vtx3(-1,  1, -1)
      gl.vtx3( 1,  1, -1)
      gl.vtx3( 1, -1, -1)
      
      glNormal3d(1, 0, 0)
      gl.vtx3( 1, -1, -1)
      gl.vtx3( 1,  1, -1)
      gl.vtx3( 1,  1,  1)
      gl.vtx3( 1, -1,  1)
      
      glNormal3d(-1, 0, 0)
      gl.vtx3(-1, -1, -1)
      gl.vtx3(-1, -1,  1)
      gl.vtx3(-1,  1,  1)
      gl.vtx3(-1,  1, -1)
      
      glNormal3d(0, 1, 0)
      gl.vtx3(-1,  1, -1)
      gl.vtx3(-1,  1,  1)
      gl.vtx3( 1,  1,  1)
      gl.vtx3( 1,  1, -1)
      
      glNormal3d(0, -1, 0)
      gl.vtx3(-1, -1, -1)
      gl.vtx3( 1, -1, -1)
      gl.vtx3( 1, -1,  1)
      gl.vtx3(-1, -1,  1)
    end
  end
end

reshape = proc do |w, h|
  a = 3
  z = 20
  
  if w <= h
    glOrtho(-a, a, -a * h.to_f / w, a * h.to_f / w, -z, z)
  else
    glOrtho(-a * w.to_f / h, a * w.to_f / h, -a, a, -z, z)
  end
end

Opal.app width: 500, height: 500, buffering: GLUT_DOUBLE, depth: GLUT_DEPTH do |g|
  gl = g
  g.clear_color = [0.2, 0, 0.3]
  g.clear_color_alpha = 1
  mySetLight.call
  glShadeModel(GL_FLAT)
  gluLookAt(0, 0, 3, 0, 0, 0, 0, 1, 0)
  
  g.display = display
  g.reshape = reshape
  g.repeat(30) do
    gYaw = (gYaw + 1) % 360
    gPitch = (gPitch + 1) % 360
    g.redraw
  end
  g.keyboard = proc {|key| exit if key == " "}
end



inserted by FC2 system