[CG実習 >  CG実習 課題(2022) >  マテリアルの制御 ]

[課題08]: マテリアルの制御

課題

ティーポットを素材として, マテリアルを変更して,それを適用した結果をマテリアルの値とともに表示するプログラムを作成して下さい.
ティーポット

物体のマテリアルのパラメタをそれぞれキー操作で増減させて,表示させるティーポットに変更結果を適用するようにしてください. パラメタは次の表に示す通り10個あります. 具体的には,拡散反射(diffuse),鏡面反射(specular),環境光反射(ambient)がそれぞれR,G,Bで3成分ずつ,それに鏡面度(shininess)を加えて全部で10個です. 鏡面度は0-128の値で指定します.その他の成分は0-1で指定します.

拡散反射(diffuse):R,G,B0.0--1.0
鏡面反射(specular):R,G,B0.0--1.0
環境光反射(ambient):R,G,B0.0--1.0
鏡面度(shininess)0.0--128.0

サンプルプログラム(テンプレート)

つぎにティーポットを描くサンプルプログラムを示します. このプログラムをテンプレートとして利用することで,課題のプログラムを作り上げることができるでしょう.

  teapot.rb

このプログラムは次のように操作します.

文法事項

今回の課題で以下の式を新たに導入しています.

修飾キー(Shift,Ctrl,Alt)の活用

キーボード入力コールバックにおいて,GLUT.GetModifiersというメソッドで, [Shift],[Ctrl],[Alt]キーが押されているかどうかをチェックできます.


  mod = GLUT.GetModifiers()
  if (mod & GLUT::ACTIVE_ALT)!=0
    #
    # [Alt]が押されている場合の処理
    #
  end

  if (mod & GLUT::ACTIVE_CTRL)!=0
    #
    # [Ctrl]が押されている場合の処理
    #
  end

  if (mod & GLUT::ACTIVE_SHIFT)!=0
    #
    # [Shift]が押されている場合の処理
    #
  end

なお[Shift],[Ctrl]を押しながらキーを押すとイベントとして発生するkeyが変化する場合がありますので注意してください. たとえば[Shift]+[a]の場合,keyは"A"に変わります(小文字→大文字). また[Ctrl]+[x]の場合,keyは「24番の文字」になります. これは「key.ord == 24」で判定できます(他の判定方法もあります).


  when key 
  case 'a'

    # [a]の場合

  case 'A'

    # [A](=[Shift]+[a])の場合

  case 'x'  

    # [x]の場合

  end

  when key.ord
  case 0x1b
    # [ESC]の場合
  case 0x18 # 0x18 == 24
    # [Ctrl]+[x]の場合
  end
  

(一時的に)キーのデータを端末に表示させてみれば,どういうキーイベントが発生しているのかを知ることができます.


keyboard = Proc.new { |key,x,y| 

  # keyとその番号(key.ord)を端末に表示する
  p [:key,key,:ord,key.ord]

}

文字の表示

画面に文字(英数字,記号)を表示するには,次のようにdrawStringメソッドを利用します. 残念ながら日本語を表示することはできません. 以下の「注意:文字表示のための追加設定」も参照して下さい.


# 次のライブラリを利用する
require 'cg/bitmapfont'

 :

display = Proc.new {

   # x,y:  表示位置(世界座標; z = 0)
   #       (※ ここで指定する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(x,y,str,font)

   # 直前に書いた文字列の続きに別の文字列を表示する場合は次のメソッドを利用する
   drawStringCont(str2,font)

   # 例
   drawString(0.0,0.0,"message",GLUT::BITMAP_9_BY_15)
   drawStringCont(" another message",GLUT::BITMAP_9_BY_15)

   # __theta,__phiという(実数値)変数が定義されているとする.
   # それぞれを全体6桁で小数点以下2桁で取り込んだ文字列を作る.
   str = "(theta,phi)=(%6.2f %6.2f)" % [__theta,__phi]

   # 生成された文字列strを表示する
   drawString(-0.9,0.9,str,GLUT::BITMAP_TIMES_ROMAN_24)

}

数値を取り込んだ文字列を作る方法

上の例でも示している通り,(表示桁数を指定して)数値を取り込んだ文字列を作ることができます.



 # サンプルデータ
 x=1.234
 y=0.456
 z=2

 # xの小数点以下を2桁までを文字列strを生成する
 # str ==> "1.23"
 str = "%.2f" % x 
 
 # xを(小数点をふくめて)全体で7桁,小数点以下3桁までで文字列str2を生成する
 # str2 ==> "  1.234"
 str2 = "%7.3f" % x 

 # xの小数点以下を2桁,yの小数点以下を3桁までを取り込んで文字列str3を作る
 # str3 ==> "Data: [X=1.23] [Y=0.456]"
 str3 = "Data: [X=%.2f] [Y=%.3f]" % [x,y]

 # x,y,zのそれぞれを小数点以下2桁まで取り込んで文字列str4を作る
 # str4 ==> "v=(1.23,0.46,2.00)"
 str4 = "v=(%.2f,%.2f,%.2f)" % [x,y,z]


いずれの場合も文字列"..."の中の「%」で始まる部分で指定された形式で,数値が当てはめられます.このとき小数点以下は(必要に応じて)四捨五入されます. 「%」で始まる部分以外は文字がそのまま入ります.

上の例に示している通り,当てはめる値が一つの場合は,(文字列につづく「%」の後に)そのままデータを指定します. 複数の値を取り込む場合,データは配列で指定します.

[注意] 文字表示のための追加設定

文字の表示においてもモデル・ビュー変換と投影変換が適用されます. Zバッファによる処理も行われます. またシェーディング処理を行っている場合には,その影響も受けます. これらの効果を考慮しないと思わぬ結果になりえます.

そこでティーポットと同じ画面上に文字を表示するときには, 文字の表示処理の前後で次のような処理を行って下さい. こうすることで文字は2次元平面上に配置するものと考えることができます. また文字色はGL.Colorで指定できます.


    GL.Disable(GL::DEPTH_TEST)    # ZバッファをOFFにする
    GL.Disable(GL::LIGHTING)      # シェーディングをOFFにする(GL.Colorで文字の色を決めるようにする)
    GL.PushMatrix()               # 現在のモデル・ビュー変換行列の退避
    GL.LoadIdentity()             # モデル・ビュー行列の初期化
    GL.MatrixMode(GL::PROJECTION) # 投影変換行列モードに設定
    GL.PushMatrix()               # 現在の投影変換行列の退避
    GL.LoadIdentity()             # 投影変換行列の初期化
     
  # ここで文字の表示を行う
  # -1 <= x <= 1,-1 <= y <= 1の範囲で二次元平面上に描くと考えればよい
    # 色はGL.Colorで指定する
    
    GL.PopMatrix()                # 退避しておいた投影変換行列に戻す
    GL.MatrixMode(GL::MODELVIEW)  # モデル・ビュー行列モードに戻す
    GL.PopMatrix()                # 退避しておいたモデル・ビュー変換行列に戻す
    GL.Enable(GL::LIGHTING)       # シェーディングをONにする
    GL.Enable(GL::DEPTH_TEST)     # ZバッファをONにする

ここで使われているGL.PushMatrix,GL.PopMatrixについては「形状モデリング」で詳しく取り上げます.

[参考] 文字の表示位置の制御

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

なおdrawStringにつづいてdrawStringContを使うときに, drawStringContの直前でGL.Colorで色を変えても反映されないという不具合が分かっています. そこで文字列の途中で色を変える場合にはdrawStringContを使う代わりに(スマートではありませんが)drawStringで表示する位置を調整して下さい.

上のように二次元平面上に文字を描くように設定をしたとき, 描画される文字列の世界座標系(2次元座標系)でのサイズは,次のrasterSizeメソッドで知ることができるようになっています.


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

  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)  

[CG実習 >  CG実習 課題(2022) >  マテリアルの制御 ]