[CG実習 >  CG実習 課題(2025) >  [課題06] 自由制作(アニメーション・インタラクション) ]

[課題06] 自由制作(アニメーション・インタラクション)

課題

アニメーションとインタラクションの機能をもったCGプログラムを作成して, プログラムについての説明をプログラムの先頭に書き加えて提出してください. インタラクションの機能をもたせて,アニメーションを行うという条件でテーマは自由です.

プログラムの説明の記述要領

プログラムについての説明を,次の要領でプログラムの先頭に記載してください.

=begin

所属:     都学部1回生
氏名:     左京太郎
学生番号:  0123456789

①プログラムの概要
②プログラムの操作方法
③自己評価(100点満点)
④工夫した点,アピールしたい点など
⑤さらに改善・拡張が考えられる点など

感想など(任意)

=end

テンプレート

次のテンプレートを提供します. ファイル名を適宜変更して使ってください.

技術情報

技術要素をいくつか紹介します.

円や円弧などの図形

OpenGLには円,円板あるいは円弧,扇形を描くプリミティブはありません. この授業では次のようにしてこれらを描画できるようにしています.


require 'opengl'
require 'glut'
require 'cg/mglutils' 

  # 円板と円
  # MGLUtils.disc([cx,cy],radius)
  # MGLUtils.circle([cx,cy],radius)
  #   [cx,cy]: 中心位置
  #   radius:  半径
  MGLUtils.disc([-0.5,0.5],0.3) 
  MGLUtils.circle([-0.5,-0.5],0.3) 

  # 扇形と円弧
  # MGLUtils.fan([cx,cy],radius,angle_start,angle_end) 
  # MGLUtils.arc([cx,cy],radius,angle_start,angle_end) 
  #   [cx,cy]: 中心位置
  #   radius:  半径
  #   angle_start: 開始位置(x軸の正の方向を基準とした角度を度で指定)
  #   angle_end:   終了位置(x軸の正の方向を基準とした角度を度で指定)
  MGLUtils.fan([0.5,0.5],0.4,30,150) 
  MGLUtils.arc([0.5,-0.5],0.4,-30,30) 


乱数

プログラムにランダム性を取り入れるには,次のメソッドを利用します.


  rand(n) # 1,...,n-1のいずれかの整数をランダムに返す(nは正の整数とする)
  rand(1..n) # 1,...,nのいずれかの整数をランダムに返す(nは正の整数とする)
  rand(0) # 0以上1未満の数をランダムに返す

文字の表示

画面に文字(英数字,記号)を表示するには,次のようにdrawStringメソッドを利用します. 残念ながら日本語を表示することはできません.


require 'opengl'
require 'glut'
require 'cg/mglutils' 
require 'cg/bitmapfont' # このライブラリを追加で利用する

 :

display = Proc.new {

   # drawString(x,y,str,font)
   # x,y:  表示位置(画面内での描画に使う世界座標系)
   # str:  表示する文字列
   # font: フォント(以下のいずれかを指定する)
   #         GLUT::BITMAP_9_BY_15
   #         GLUT::BITMAP_8_BY_13
   #         GLUT::BITMAP_TIMES_ROMAN_10
   #         GLUT::BITMAP_TIMES_ROMAN_24
   #         GLUT::BITMAP_HELVETICA_10
   #         GLUT::BITMAP_HELVETICA_12
   #         GLUT::BITMAP_HELVETICA_18
   #
   drawString(0.0,0.0,'Origin',GLUT::BITMAP_9_BY_15)

   # drawStringCont(str,font)
   # str:  表示する文字列
   # font: フォント(以下のいずれかを指定する)
   #
   # 直前に書いた文字列の続きに別の文字列を表示する場合は,
   # このメソッドが利用できる.
   #
   drawStringCont(' Point',GLUT::BITMAP_9_BY_15)

   # "..."で括られたメッセージに#{式}と書くと,式の値を計算した結果が
   # メッセージに取り込まれる.
   # 次の例では「norm = 2.2360...」のように表示される
   x = 2; y = 1
   drawString(0.5,0.5,"norm = #{Math.sqrt(x*x+y*y)}",GLUT::BITMAP_9_BY_15)

}

drawStringdrawStringContメソッドは,この実習用に用意したもので,OpenGLで標準に使えるものではありません(Ruby/OpenGLにも含まれていません).

なおdrawStringにつづいてdrawStringContを使うときに, drawStringContの直前でGL.colorで色を変えても反映されないようになっています. 文字列の途中で色を変える場合にはdrawStringContを使う代わりに(スマートではありませんが)表示位置を調整してdrawStringを使ってください. 描画される文字列の世界座標系(画面内の世界での座標系)での(おおよその)サイズを次のメソッドで知ることができます.


  # obj:   描画する文字列あるいは文字数
  # font:  描画に用いるフォント
  # range: ウインドウの(左端/上端)から(右端/下端)までの世界座標系での長さ
  # wsize: ウインドウの大きさ
  # dir: BITMAP_DIM_WIDTH または BITMAP_DIM_HEIGHT
  # rasterSize(obj,font,range,wsize,dir)

  WSIZE=800

  # 描画する文字列の世界座標系での(おおよその)横幅を求める(BITMAP_DIM_WIDTH)
  # 'hello!'という文字列を「GLUT::BITMAP_9_BY_15」で描画する
  # ウインドウの左端から右端までの世界座標系での長さは2.0であるとする(-1≦x≦1)
  # ウインドウの大きさ(横幅)はWSIZE(=800)とする
  # 得られた値をswに代入する
  sw = rasterSize('hello!',GLUT::BITMAP_9_BY_15,2.0,WSIZE,BITMAP_DIM_WIDTH)

  # 描画する文字列の世界座標系での(おおよその)横幅を求める(BITMAP_DIM_WIDTH)
  # 「GLUT::BITMAP_HELVETICA_18」で12文字分描画する場合を考える
  # ウインドウの左端から右端までの世界座標系での長さは2.0だとする(-1≦x≦1)
  # ウインドウの大きさ(横幅)はWSIZE(=800)とする
  # 得られた値をswに代入する
  sw = rasterSize(12,GLUT::BITMAP_HELVETICA_18,2.0,WSIZE,BITMAP_DIM_WIDTH)  

  # 描画する文字列の世界座標系での(おおよその)高さを求める(BITMAP_DIM_HEIGHT)
  # 「GLUT::BITMAP_HELVETICA_18」での縦方向で3文字分の高さを求める
  # ウインドウの上端から下端までの世界座標系での長さは2.0だとする(-1≦y≦1)
  # ウインドウの大きさ(高さ)はWSIZE(=800)とする
  # 得られた値をshに代入する
  sh = rasterSize(3,GLUT::BITMAP_HELVETICA_18,2.0,WSIZE,BITMAP_DIM_HEIGHT)  

ウインドウ座標系

画面のマウスポインタの位置(x,y)はウインドウを基準とした座標系で表現されます. ウインドウの左上角が原点で,x座標は右向き,y座標は下向きにとられます. ウインドウのサイズの単位が座標系の単位と一致していて, 座標の値としては整数のみが得られます. ここで得られる値は,世界座標系(画面内で図形の描画に使う座標系)ではないので注意が必要です.

マウスの動きの扱い

マウスがドラッグされている(ボタンが押された状態でマウスが動いている)ときにマウスポインタの位置を追跡するコールバック(MotionFunc)が利用できます. このコールバックとマウスボタンのコールバック(MouseFunc)を利用することで, ドラッグによる図形の移動などを実現できます.


  #### モーション(マウスドラッグ)コールバック ########
  motion = Proc.new {|x,y|
     # (x,y): マウスポインタの位置(ウインドウ座標)
    
  }

  :

  # コールバックの登録
  GLUT.MotionFunc(motion)

すでに説明したように,マウスポインタの位置(x,y)はウインドウを基準とした座標系で与えられますので注意してください. 次にサンプルプログラムを示します.

マウスのボタンが押されていないときにマウスポインタの位置を追跡するコールバック(PassiveMotionFunc)も利用できます. 仕様はMotionFuncと同様です.


  #### マウス追跡コールバック ########
  p_motion = Proc.new {|x,y|
     # (x,y): マウスポインタの位置(ウインドウ座標)
    
  }

  :

  # コールバックの登録
  GLUT.PassiveMotionFunc(p_motion)

次にサンプルプログラムを示します.

特別なキーの扱い

カーソルキー[↓][↑]やファンクションキー[F1]...[F12],ホームキー[Home]などの通常の文字以外のキー入力はキーボードコールバック(KeyboardFunc)では扱えません(asciiのマニュアルにキーコードが記載されていません). それらは特殊キー用のコールバック(SpecialFunc)を別途作成して,登録します.

修飾キーの状態取得

GLUT.GetModifiersというメソッドで, [Shift],[Ctrl],[Alt]キー(修飾キー)が押されているかどうかをチェックできます.

keyboard = Proc.new { |key,x,y|
  modifiers=GLUT.GetModifiers()
  if (modifiers&GLUT::ACTIVE_CTRL)!=0
    # [Ctrl]が押されているときの処理を書く(次の場合は端末に「Ctrl」と表示)
    print 'Ctrl '
  end
  if (modifiers&GLUT::ACTIVE_ALT)!=0
    # [Alt]が押されているときの処理を書く(次の場合は端末に「Alt」と表示)
    print 'Alt '
  end
  if (modifiers&GLUT::ACTIVE_SHIFT)!=0
    # [Shift]が押されているときの処理を書く(次の場合は端末に「Shift」と表示)
    print 'Shift '
  end
  puts "key=#{key}" # keyの値を端末に表示する
} 

これを使うと[Shift],[Ctrl],[Alt]という修飾キーを押す場合と押さない場合で処理を分けることができます. なお修飾キーを押したときと押さないときとでkeyの値が変わる場合は,そのことも考慮する必要があります. たとえば[Shift]+[a]の場合,[a]が[A]に変わります('a'と'A'は異なります).

数学の関数と定数

関数,定数Rubyでの式備考
√xMath.sqrt(x)
sin xMath.sin(x)xはラジアン
cos xMath.cos(x)xはラジアン
tan xMath.tan(x)xはラジアン
log xMath.log(x)自然対数
log10xMath.log10(x)常用対数
exMath.exp(x)
πMath::PI「PI」は大文字のpi
xyx**y
xの絶対値x.abs

色表

色指定のサンプルを示した表を準備しました.

[CG実習 >  CG実習 課題(2025) >  [課題06] 自由制作(アニメーション・インタラクション) ]