[ CG実習 >  CG実習 課題(2025) >  [課題01] OpenGLでの2次元図形の描画 ]

[課題01] OpenGLでの2次元図形の描画

課題

次に示すサンプルプログラムを参考にして, さまざまな図形を組み合わせて,自由に絵を描いて画像を生成するプログラムを作成し, そのプログラムを提出してください

注意

プログラムを提出する前に「課題提出に関する注意」をよく読んでおいてください

サンプルプログラム

次のサンプルプログラムでは三色旗(フランスの国旗)を描きます.

次に別のサンプルを示します.上向き・下向きの矢印のボタンが並べて配置してあるパネルをイメージしています.

次は円板,扇形,円弧,円を描くサンプルプログラムです.

サンプルプログラムは次のように実行します.

$ ruby  tricolore.rb
$ ruby  updown.rb
$ ruby  arcs.rb

いずれもウインドウ上で何かキーを押すとプログラムは終了します.

課題で作成するプログラムも同様に実行して操作することになります.

ファイルのダウンロード

この授業のサンプルファイル等はダウンロードして利用してください. ブラウザの画面で開くと「文字化け」します. ダウンロードするにはリンクを右クリックして「名前を付けて保存」を 選んでください.ファイル名は必要に応じて変更してください. なおダウンロード先は「Home」としてください.

課題の進め方

この課題では次のテンプレートに基づいて何らかの絵を描くプログラムを作成してください.

このテンプレートはサンプルプログラムから図形のデータを取り除いたものです. ダウンロードするときにファイル名は適宜変えてください. 課題で作成するプログラムには,基本的に自由に名前をつけて構いません. ただし日本語や空白文字を入れないようにしてください. またRubyのファイルであることを示す拡張子(.rb)を名前の最後に必ずつけてください. Emacsの利用法については前回の課題のページ(サンプルプログラムによるテスト)も参照してください.

プログラムで絵を描くには,次に示す部分に図形プリミティブ等のデータを適宜追加してください.



# 描画処理
display = Proc.new {
  GL.Clear(GL::COLOR_BUFFER_BIT) # 背景のクリア

  ###
  ### GL.Clear...GL.Flushの間に図形プリミティブ等のデータを並べて絵を描く
  ### (この説明文は削除して構いません)
  ###

  GL.Flush() # 描画強制実行
}

図形の描き方については,授業資料の「図形プリミティブ」を参考にしてください. なお画面に入るのは次の範囲です.


    -1 ≦ x ≦ 1 かつ -1 ≦ y ≦ 1 

図形はいくつでも描けます. プログラムにはそれぞれの図形のデータを順に並べていきます. 図形プリミティブは入れ子にはできません(つまり図形プリミティブの内部に別の図形プリミティブは定義できません). なおテンプレートに記述してあるように,描画処理では,まず最初に「背景のクリア」(GL.Clear)を行います.つづいて各図形のデータを並べた後,最後に「描画の強制実行」(GL.Flush)を行うようにしてください.


display = Proc.new {
  GL.Clear(GL::COLOR_BUFFER_BIT) 

  GL.Begin(GL::TRIANGLES)
  
    :

  GL.End()

  GL.Begin(GL::QUADS)
  
    :

  GL.End()

  GL.Flush() 
}

図形は重ね描きできます.描画した図形に重なりがある場合は, 後から描いた図形で画面が上書きされます.

作成したプログラムは,上に示したサンプルプログラムと同様に実行します. ただ課題では,プログラムを一気に書き上げて,一度実行して完成とはならないことが,ごく一般的です. プログラムに追加,修正を施しながら,段階的に完成に近づけていくことになるでしょう.

プログラムを変更(+保存)して,変更したプログラムでの描画の結果を確認するには,プログラムを実行し直す必要があります(実行中のプログラムには変更は反映されません). プログラムを再実行しようとするとき,変更前のプログラムが実行したままであれば(ウインドウを開いたままであれば),そちらを先に終了してください. プログラムの実行が終了していないと,端末で文字入力が受け付けられません.

プログラムを端末から実行して,それを終了しないで,さらに別のプログラムを実行する方法もあります.

描画範囲の指定

次のように設定を追加することで描画対象となる範囲を自分で指定できます.

require 'opengl'
require 'glu' # この行を先頭の「require」に追加する
require 'glut'
require 'cg/mglutils'

   :
   :
   :

# ファイルの末尾に次の行(GLU.Ortho2D)を追加する
GLU.Ortho2D(-2.0,2.0,-2.0,2.0)  # 描画する範囲を指定する(-2≦x≦2,-2≦y≦2)
GLUT.MainLoop()          # イベントループ開始

色の指定

色はR,G,B(赤,緑,青)の組み合わせで指定します(サンプルプログラムを参考にしてください). 値はいずれも0.0-1.0の範囲で指定します.


    GL.Color(0.000,0.125,0.624) # 三色旗の「青」
    GL.Color(1.0,1.0,1.0)       # 白
    GL.Color(0.957,0.165,0.255) # 三色旗の「赤」

なお指定する際にR,G,Bの組み合わせで自分の表現したい色を記述することは必ずしも簡単ではないでしょう. そこで,Colortable(色表)と色データを調べられるプログラムを用意しましたので必要に応じて使ってみてください.

背景色の指定

画面の背景色を指定できます. 本科目では背景色は次のように指定します(OpenGLの標準の方法ではありません).


  MGLUtils.bgcolor(0.0,0.0,0.0) # 背景色を設定(黒) 

色の指定方法はGL.Colorと同じです. 背景色はテンプレート(simple_template.rb)でも設定してありますので参考にしてください.

円の描画

図形プリミティブによって円を近似的に描くことができます. しかし今回学習した方法で円に見える図形を描くにはかなり手間が必要となることでしょう. そこで本科目の実習では次のようにして(近似的な)円を簡単に描けるようにしています. なお円の描画処理は「GL.Begin..GL.End」で囲う必要はありませんので注意してください.


### 以下はいずれも「display」の中に記述する
### GL.Begin...GL.Endで囲わずに記述する
### 色はプリミティブと同様にGL.Colorで指定する

# (x,y)を中心として半径rの円(円周)を描く
MGLUtils.circle([x,y],r)

# (x,y)を中心として半径rの円板(塗りつぶした円)を描く
MGLUtils.disc([x,y],r)

# (x,y)を中心として半径rでa0の方向からa1の方向までの円弧を描く.a0,a1はx軸の正の方向を0として左回りでdegree(0-360)を単位として指定する.
MGLUtils.arc([x,y],r,a0,a1)

# (x,y)を中心として半径rでa0の方向からa1の方向までの扇形を描く.a0,a1はx軸の正の方向を0として左回りでdegree(0-360)を単位として指定する.
MGLUtils.fan([x,y],r,a0,a1)

文字の描画

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


require 'opengl'
require 'glut'
require 'cg/mglutils'
require 'cg/bitmapfont' # この行を先頭の「require」に追加する

 :

display = Proc.new {
  GL.Clear(GL::COLOR_BUFFER_BIT) # 背景のクリア

   # x,y:  表示位置
   #       (※ ここで指定する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)

   # (例) 画面左上に黄色で「Hello, World!」と描く
   GL.Color(1.0,0.9,0.0)
   drawString(-0.9,0.9,"Hello, World!",GLUT::BITMAP_TIMES_ROMAN_24)

  GL.Flush() # 描画強制実行
}

その他

プログラムで利用するかもしれない情報を挙げておきます.

Tips

課題に取り組むにあたって,以下も適宜参照してください.

履歴とファイル名の補完について,説明を簡単にするために「端末の機能」としていますが, 正しくは端末上で動作しているシェル(shell)の機能です.
[ CG実習 >  CG実習 課題(2025) >  [課題01] OpenGLでの2次元図形の描画 ]