mieki256's diary



2017/02/07(火) [n年前の日記]

#1 [ruby][gosu] Ruby + Gosu + opengl の動作確認

Windows10 x64上で、 _Ruby 2.2.6p396 mingw32 + _gosu 0.11.1 + _opengl 0.9.2 を使って、OpenGLの描画ができそうか実験。

ちなみに、Ruby というのはプログラミング言語の一つで…。そのRubyを使って2Dゲームを作れるライブラリとして、gosu というのがあって。という説明でいいのだろうか。

Ruby を使ってゲームを作れるライブラリとしては、他にも DXRuby があるけれど。 こんな説明で合ってるのかしらん。

OpenGLが動くかどうかだけを確認。 :

まずは gosu と関係なく、opengl というライブラリが動くかどうかを確認。以下のページを参考に、というかそのままコピペさせてもらって試してみたり。ありがたや。

_RubyでOpenGL - verus diary
_ruby-openglでお手軽3Dプログラミング | Gemの紹介 | DoRuby
_MF / Ruby で OpenGL

opengl、glu、glut なるライブラリをインストールする。Rubyがインストールされてる環境なら、以下でインストールできる。
gem install opengl
gem install glu
gem install glut

ところで、GLU とか GLUTって何だろう?

_OpenGL Utility Toolkit - Wikipedia によると…。
  • OpenGL Utility Library の略で「GLU」。
  • OpenGL Utility Toolkit の略で「GLUT」。
OpenGLだけでは機能が少なくてちょっと不便なところがあるので、もうちょっと便利に簡単に使えるように機能を追加してくれるものらしい。

さておき。動作確認したソースはこんな感じに。

_opengl_glut_only_test.rb
# gosu + opengl のテスト
# とりあえず、opengl + glut のテストだけをしてみる。

require "opengl"
require "glut"
require "glu"

# 表示処理
display = proc {
  GL.Clear(GL::COLOR_BUFFER_BIT)  # カラーバッファをクリア
  GL.Color3f(0.0 , 1.0 , 0.0)  # 色を指定
  GLUT.WireTeapot(0.5)  # ティーポットを表示
  GLUT.SwapBuffers()  # 画面を入れ替え(ダブルバッファ)
}

# ウインドウが変形したり最初に生成された際に呼ばれる処理
reshape = proc { |w, h|
  GL.Viewport(0, 0, w, h)  # ビューポートを設定

  GL.MatrixMode(GL::GL_PROJECTION)  # 演算ターゲットを射影行列に
  GL.LoadIdentity()  # 変換行列を単位行列に
  GLU.Perspective(30.0, w.to_f/h, 1.0, 100.0)  # 透視投影を指定

  GL.MatrixMode(GL::GL_MODELVIEW)  # 演算ターゲットをモデルビュー行列に
  GL.LoadIdentity()  # 変換行列を単位行列に

  GLU.LookAt(3.0, 2.0, 1.0, # カメラ位置
             0.0, 0.0, 0.0, # 注視点の位置
             0.0, 1.0, 0.0 # どっちが上かを指定
             );
}

# 一定時間ごとに呼ばれる処理
timer = proc {
  GL.Rotate(2.0, 0.0, 1.0, 0.0)  # Y軸を回転 (ang, x, y, z)
  GLUT.PostRedisplay()  # 再描画を要求
  GLUT.TimerFunc(16, timer, 0)  # 16ms毎に呼び直す
}

GLUT.Init()  # GLUTの初期化
GLUT.InitWindowSize(512, 512)  # ウインドウサイズを指定
GLUT.CreateWindow("OpenGL:Teapot") # ウインドウを生成

GLUT.DisplayFunc(display) # ディスプレイコールバックの登録
GLUT.ReshapeFunc(reshape)  # リシェイプコールバックの登録
GLUT.TimerFunc(10, timer, 0)  # 一定時間ごとに呼ばれる処理を登録

GL.ClearColor(0.2,0.2,0.2,0.0)  # 画面クリア時の色を指定

GLUT.MainLoop()  # メインループ

こういう結果になった。ティーボットが表示された。



ソースの中にコメントを書きまくったので、ソースを見れば何をやってるか大体分かるかなと。

gosu + opengl で3D描画の動作確認。 :

gosu と opengl を組み合わせて、3D描画ができるのか確認してみる。gosu + opengl ができると何が嬉しいかと言うと…。例えば、「背景は3D描画でカッコよく」「だけど手前は2D描画・2Dゲームで」みたいなことができる、かもしれない。要は、2Dゲーム内でも演出として3D描画を使えるかもしれないよね、みたいな。

gosu には、 _gosu-examples という、サンプルファイルがいくつか入ったライブラリ(?)が用意されている。その中の _gosu-examples/opengl_integration.rb が、gosu + opengl のサンプルになっていて。

件のサンプルの中から、gosu の描画関係を省いて、opengl の描画部分だけを残して色々弄って動作確認してみたり。

ちなみに、gosu のインストールは以下でできる。
gem install gosu

動作確認に使ったソースは以下。

_gosu_opengl_test1.rb
# gosu + opengl の動作確認
# gosu-examplesの opengl_integration.rb を弄って、
# OpenGL 絡みの部分だけを列挙
# 横スクロールで地形の上を進むイメージで描画

require 'gosu'
require 'gl'

WIDTH, HEIGHT = 640, 480

# OpenGLを使って背景描画するためのクラス
class GLBackground
  # Height map size
  POINTS_X = 16
  POINTS_Y = 16

  # Scrolling speed
  SCROLLS_PER_STEP = 20

  # 初期化
  def initialize
    @image = Gosu::Image.new("uv.png", :tileable => true)  # テクスチャ読み込み
    @scrolls = 0
    @height_map = Array.new(POINTS_Y) { Array.new(POINTS_X) { rand } }
  end

  # 更新処理
  def update
    @scrolls += 1
    if @scrolls == SCROLLS_PER_STEP
      @scrolls = 0
      @height_map.shift
      @height_map.push Array.new(POINTS_X) { rand }
    end
  end

  # 描画処理
  def draw(z)
    # Gosu.gl(z値)でOpenGLの描画を行う
    # 描画後、Gosuの描画ができるようにしてくれるらしい
    Gosu.gl(z) { exec_gl }
  end

  private

  include Gl

  # OpenGL関係の処理
  def exec_gl
    glClearColor(0.2, 0.2, 0.2, 1.0)  # 画面クリア色を指定 (r,g,b,a)
    glClearDepth(1.0)  # デプスバッファをクリア
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 画面クリア

    # テクスチャ情報が取得できないなら何もせずに戻る
    info = @image.gl_tex_info
    return unless info

    # 奥行き比較関数の種類を指定。デフォルトではGL_LESSが指定されてるらしい
    # glDepthFunc(GL_GEQUAL)
    glDepthFunc(GL_LESS)

    glEnable(GL_DEPTH_TEST)  # デプスバッファを使う
    # glEnable(GL_BLEND)  # アルファブレンドを有効化

    glMatrixMode(GL_PROJECTION)  # 透視投影の設定
    glLoadIdentity
    # glFrustum(-0.10, 0.10, -0.075, 0.075, 1, 100)
    glFrustum(-0.04, 0.04, -0.075, 0.075, 0.1, 100)

    glMatrixMode(GL_MODELVIEW)  # モデルビュー変換の指定
    glLoadIdentity
    glRotate(35.0, 1.0, 0.0, 0.0) # 回転させる (ang, x, y, z)
    glTranslate(0, -0.35, -0.8)  # 位置をずらす

    glEnable(GL_TEXTURE_2D)  # テクスチャマッピングを有効化
    glBindTexture(GL_TEXTURE_2D, info.tex_name)

    # スクロールオフセット値を得る
    offs_y = 1.0 * @scrolls / SCROLLS_PER_STEP

    dy = 0.2
    dz = 1.5

    0.upto(POINTS_Y - 2) do |y|
      0.upto(POINTS_X - 2) do |x|
        glBegin(GL_TRIANGLE_STRIP)  # 三角ポリゴンを連続で描画

        px0 = -0.5 + (x - 0.0) / (POINTS_X - 1)
        px1 = -0.5 + (x + 1.0) / (POINTS_X - 1)
        py0 = -0.5 + (y - 0.0 - offs_y) / (POINTS_Y - 2)
        py1 = -0.5 + (y + 1.0 - offs_y) / (POINTS_Y - 2)

        z = @height_map[y][x]
        # glColor4d(1, 1, 1, z)  # 透過度を指定してフォグに近い効果を出してる
        glTexCoord2d(info.left, info.top)  # テクスチャ座標を指定
        # 頂点を指定
        glVertex3d(py0, z * dy, px0 * dz)

        z = @height_map[y + 1][x]
        # glColor4d(1, 1, 1, z)
        glTexCoord2d(info.left, info.bottom)
        glVertex3d(py1, z * dy, px0 * dz)

        z = @height_map[y][x + 1]
        # glColor4d(1, 1, 1, z)
        glTexCoord2d(info.right, info.top)
        glVertex3d(py0, z * dy, px1 * dz)

        z = @height_map[y+1][x + 1]
        # glColor4d(1, 1, 1, z)
        glTexCoord2d(info.right, info.bottom)
        glVertex3d(py1, z * dy, px1 * dz)

        glEnd
      end
    end
  end
end

class MyWindow < Gosu::Window

  # 初期化
  def initialize
    super WIDTH, HEIGHT
    self.caption = "Ruby + Gosu + OpenGL Test"  # ウインドウタイトルを指定
    @gl_background = GLBackground.new
  end

  # 更新処理
  def update
    @gl_background.update
  end

  # 描画処理
  def draw
    @gl_background.draw(0)
  end
end

MyWindow.new.show
テストで使った画像は以下。スクリプトと同じフォルダに置いておく。
uv.png
_uv.png

こういう結果に。それっぽく描画されている、ような気がする。



gosu で opengl を使う手管は…。基本的には、gosu と gl を require して。
require 'gosu'
require 'gl'

OpenGLで描画したい箇所で、
Gosu.gl(z値) { OpenGL描画処理をする関数 }
を呼べば、OpenGL で描画してくれる模様。この、Gosu.gl() は、OpenGLの描画が終わったら、その後gosuで描画できるように色々リセットしてくれるらしい。

_Method: Gosu.gl - Documentation for gosu/gosu (master)

上記のドキュメントによると、注意事項として…。
  • 「gosu自体は、OpenGL APIを提供してないから、OpenGLを使えるようにする何かと一緒に使ってね。例えば ruby-opengl と組み合わせて使うとか」
  • 「Gosu.gl() の中で gosuの描画機能は使えないよ」
  • 「Gosu.gl() は Gosu::Window の draw() の中で呼んでね」
と書いてある、のかな。たぶん。ちなみに ruby-opengl は開発停止状態なので、opengl を使うべき、だろうけど。

また、 _GitHub - tjbladez/gosu-opengl-tutorials: Fun with opengl, gosu and ruby によると、もうちょっと違う書き方もできるのかもしれない。

_gosu-opengl-tutorials/lesson01.rb を眺めた感じでは、Gosu::Window を継承したクラスのdraw内で、
gl do
  ...
  ...
  ...
end
と書いて、そこで OpenGL 関係の処理を書いてある。手元で試してないので本当にその書き方で動くのかどうかは分からんけど、そういう書き方もあるっぽい、ってことで。

gosu + opengl で3D描画の動作確認その2。 :

前述のソースを流用して、もうちょっと違う処理を書いてみた。

_gosu_opengl_test2.rb
# gosu + opengl の動作確認
# gosu-examplesの opengl_integration.rb を弄って、
# OpenGL 絡みの部分だけを列挙
# 円柱の中を進むイメージで描画

require 'gosu'
require 'gl'

WIDTH, HEIGHT = 640, 480

# OpenGLを使って背景描画するためのクラス
class GLBackground

  # 初期化
  def initialize
    # テクスチャ読み込み
    @image = Gosu::Image.new("mecha.png", :tileable => true)
    @scrolls = 0
    @rot_v = 0
    @rot_v2 = 0
  end

  # 更新処理
  def update
    @scrolls += 1
    @rot_v = (@rot_v + 0.3) % 360.0
    @rot_v2 = (@rot_v2 + 0.5) % 360.0
    if @scrolls == SCROLLS_PER_STEP
      @scrolls = 0
    end
  end

  # 描画処理
  def draw(z)
    # Gosu.gl(z値)でOpenGLの描画を行う
    # 描画後、Gosuの描画ができるようにしてくれるらしい
    Gosu.gl(z) { exec_gl }
  end

  private

  include Gl

  # Scrolling speed
  SCROLLS_PER_STEP = 15

  # OpenGL関係の処理
  def exec_gl
    glClearColor(0.0, 0.0, 0.0, 1.0)  # 画面クリア色を指定 (r,g,b,a)
    glClearDepth(1.0)  # デプスバッファをクリア
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 画面クリア

    # テクスチャ情報が取得できないなら何もせずに戻る
    info = @image.gl_tex_info
    return unless info

    # 奥行き比較関数の種類を指定。デフォルトではGL_LESSが指定されてるらしい
    # glDepthFunc(GL_GEQUAL)
    glDepthFunc(GL_LESS)

    glEnable(GL_DEPTH_TEST)  # デプスバッファを使う
    glEnable(GL_BLEND)  # アルファブレンドを有効化

    glMatrixMode(GL_PROJECTION)  # 透視投影の設定
    glLoadIdentity
    glFrustum(-0.10, 0.10, -0.075, 0.075, 0.1, 100)

    glMatrixMode(GL_MODELVIEW)  # モデルビュー変換の指定
    glLoadIdentity
    glTranslate(0, 0.0, -0.8)  # 位置をずらす

    ry = 25.0 * Math.sin(@rot_v * Math::PI / 180.0)
    glRotate(ry, 0.0, 1.0, 0.0) # 回転
    rz = 20.0 * Math.sin(@rot_v2 * Math::PI / 180.0)
    glRotate(rz, 0.0, 0.0, 1.0) # 回転

    # スクロールオフセット値を得る
    offs_y = 1.0 * @scrolls / SCROLLS_PER_STEP

    glEnable(GL_TEXTURE_2D)  # テクスチャマッピングを有効化
    glBindTexture(GL_TEXTURE_2D, info.tex_name) # テクスチャを割り当て

    # テクスチャの補間を指定
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

    mx = 15.0
    my = 1.2
    mz = 1.2
    ra = @rot_v * 4.0
    lx = 28
    0.upto(lx) do |bx|
      px0 = (-0.5 + (bx - 0.0 - offs_y) / lx) * mx
      px1 = (-0.5 + (bx + 0.6 - offs_y) / lx) * mx

      aa = 15
      0.step(360, aa) do |ang|
        rad0 = (ang + ra) * Math::PI / 180.0
        pz0 = Math.cos(rad0) * mz
        py0 = Math.sin(rad0) * my

        rad1 = (ang + ra + aa) * Math::PI / 180.0
        pz1 = Math.cos(rad1) * mz
        py1 = Math.sin(rad1) * my

        glBegin(GL_TRIANGLE_STRIP)

        glTexCoord2d(info.left, info.top)
        glVertex3d(px0, py0, pz0)

        glTexCoord2d(info.left, info.bottom)
        glVertex3d(px0, py1, pz1)

        glTexCoord2d(info.right, info.top)
        glVertex3d(px1, py0, pz0)

        glTexCoord2d(info.right, info.bottom)
        glVertex3d(px1, py1, pz1)

        glEnd
      end
    end
  end
end

class MyWindow < Gosu::Window

  # 初期化
  def initialize
    super WIDTH, HEIGHT
    self.caption = "Ruby + Gosu + OpenGL Test"  # ウインドウタイトルを指定
    @gl_background = GLBackground.new
    @bg_img = Gosu::Image.new("bg.png", :tileable => true)
  end

  # 更新処理
  def update
    @gl_background.update
  end

  # 描画処理
  def draw
    @gl_background.draw(2)
    @bg_img.draw(0, 0, 0)
  end
end

MyWindow.new.show

使った画像は以下。
mecha.png
_mecha.png

こんな結果に。



手前のほうで、gosu を使ってSTGの自機だの敵だのを2D描画するだけでも、それっぽくなりそうな予感。

OpenGL描画の後ろに何かを描画。 :

OpenGLで描画したソレの後ろに星空を表示したくてアレコレ試してたのだけど、どうやら OpenGLで描画した部分の後ろ・奥・背景に、gosuで何かを描画、てなことはできないっぽい。

どうやら、OpenGLで描画したソレは、全面が不透明になるようで。gosuの描画をz値指定で奥にしようとしてもOpenGL描画部分で上書きされてしまう、ような気がする。

つまり、OpenGLで描いた何かを、gosu描画の手前に持ってくることはできない ―― 背景は全部OpenGLで、手前はgosu描画で、という形にしかできない可能性が高いなと。もしかすると回避策・解決策があるのかもしれないけど、ちょっと見つけられなかった。

以上です。

過去ログ表示

Prev - 2017/02 - Next
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project