2017/03/16(木) [n年前の日記]
#2 [ruby] Ruby + gosu + opengl でプログラマブルシェーダを利用してみる
_算譜記録帳: OpenGLでの頂点データの扱いの変化
を参考にして、OpenGL 2.0風の書き方を ―― プログラマブルシェーダ(頂点シェーダ、フラグメントシェーダ)を用意して3D描画するRubyスクリプトを書いてみたり。
正直、自分の書いたソースはグチャグチャでアレなのだけど…。Web上で紹介されてるOpenGL関係のソースは、そのほとんどがおそらくC++で書かれてるっぽいので、「Rubyで書くとこうなるよ」てな例としてアップロードしておくだけでも多少は意味があるのかなと。
 正直、自分の書いたソースはグチャグチャでアレなのだけど…。Web上で紹介されてるOpenGL関係のソースは、そのほとんどがおそらくC++で書かれてるっぽいので、「Rubyで書くとこうなるよ」てな例としてアップロードしておくだけでも多少は意味があるのかなと。
◎ まずは三角形を描画。 :
まずは、三角形のみを描画してみる。
_gosu_opengl_20a.rb
こんな感じの結果画面に。
ポイントになる部分だけをメモ。
_DXRubyのShader と同様なのだけど、Ruby + OpenGL を使って実験する際、Rubyスクリプトのソース内に、シェーダプログラム(GLSL)のソースを直接ずらずらと書けるあたりはありがたいなと。気軽に実験できるというか。
_gosu_opengl_20a.rb
こんな感じの結果画面に。
ポイントになる部分だけをメモ。
  # プログラマブルシェーダの初期化
  def init_shader
    # 頂点シェーダ(Vertex Shader)のソース
    # gl_position に (x, y, 0.0, 1.0) を渡してる
    vert_shader_src_a =<<EOS
#version 120
attribute vec2 coord2d;
void main(void) {
  gl_Position = vec4(coord2d, 0.0, 1.0);
}
EOS
    # フラグメントシェーダ(Fragment Shader)のソース
    # gl_FragColor に (r,g,b,a)=(0, 0, 1, 1) を渡してるので、青一色になる
    frag_shader_src =<<EOS
# #version 120
void main(void) {
  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
}
EOS
    # (r,g,b,a) の r,g をx,y座標で変化させるのでグラデーションになる
#     frag_shader_src =<<EOS
# #version 120
# void main(void) {
#   gl_FragColor[0] = gl_FragCoord.x / 640.0;
#   gl_FragColor[1] = gl_FragCoord.y / 480.0;
#   gl_FragColor[2] = 0.5;
#   gl_FragColor[3] = 1.0;
# }
# EOS
    # 頂点シェーダを設定
    # ----------------------------------------
    # 1. シェーダオブジェクトを作成
    vert_shader = glCreateShader(GL_VERTEX_SHADER)
    # 2. シェーダのソースを渡す
    glShaderSource(vert_shader, vert_shader_src_a)
    # 3. シェーダをコンパイル
    glCompileShader(vert_shader)
    # 4. 正しくコンパイルできたか確認
    compiled = glGetShaderiv(vert_shader, GL_COMPILE_STATUS)
    abort "Error : Compile error in vertex shader" if compiled == GL_FALSE
    # フラグメントシェーダを設定
    # ----------------------------------------
    # 1. シェーダオブジェクトを作成
    frag_shader = glCreateShader(GL_FRAGMENT_SHADER)
    # 2. シェーダのソースを渡す
    glShaderSource(frag_shader, frag_shader_src)
    # 3. シェーダをコンパイル
    glCompileShader(frag_shader)
    # 4. 正しくコンパイルできたか確認
    compiled = glGetShaderiv(frag_shader, GL_COMPILE_STATUS)
    abort "Error : Compile error in fragment shader" if compiled == GL_FALSE
    # 5. プログラムオブジェクトを作成
    @shader = glCreateProgram
    # 6. プログラムオブジェクトに対してシェーダオブジェクトを登録
    glAttachShader(@shader, vert_shader)
    glAttachShader(@shader, frag_shader)
    # 7. シェーダプログラムをリンク
    glLinkProgram(@shader)
    # 8. 正しくリンクできたか確認
    linked = glGetProgramiv(@shader, GL_LINK_STATUS)
    abort "Error : Linke error" if linked == GL_FALSE
    # 9. シェーダプログラムを適用
    glUseProgram(@shader)
    # 設定が終わったので後始末
    glDeleteShader(vert_shader)
    glDeleteShader(frag_shader)
    # シェーダに渡す属性のインデックス値(0,1,2,3等)を得る
    attr_name = "coord2d"
    @coord2d = glGetAttribLocation(@shader, attr_name)
    abort "Error : Could not bind attribute #{attr_name}" if @coord2d == -1
  end
OpenGL 2.0 でシェーダを用意する手順としては…。
- シェーダオブジェクトを作成
 - シェーダのソースを渡す
 - シェーダをコンパイル
 - 正しくコンパイルできたか確認
 - プログラムオブジェクトを作成
 - プログラムオブジェクトに対してシェーダオブジェクトを登録
 - シェーダプログラムをリンク
 - 正しくリンクできたか確認
 - シェーダプログラムを適用
 
_DXRubyのShader と同様なのだけど、Ruby + OpenGL を使って実験する際、Rubyスクリプトのソース内に、シェーダプログラム(GLSL)のソースを直接ずらずらと書けるあたりはありがたいなと。気軽に実験できるというか。
◎ もう少し複雑な描画をしてみる。 :
正直なところ、シェーダをどんな風に書けばいいのかまだ全然分かってない状態なのだけど。以下の記事で参考として紹介されてるシェーダを使わせてもらって、もう少し複雑な描画を試してみたり。
_床井研究室 - 第1回 シェーダプログラムの読み込み
_床井研究室 - 第2回 Gouraud シェーディングと Phong シェーディング
_床井研究室 - 第3回 テクスチャの参照
Rubyスクリプトは、こんな感じに。
_gosu_opengl_20g_read_json.rb
結果画面は、こんな感じに。
ということで、Ruby + gosu + opengl を使っても、OpenGL 2.0風の書き方はできるみたいだなと。
_床井研究室 - 第1回 シェーダプログラムの読み込み
_床井研究室 - 第2回 Gouraud シェーディングと Phong シェーディング
_床井研究室 - 第3回 テクスチャの参照
Rubyスクリプトは、こんな感じに。
_gosu_opengl_20g_read_json.rb
- jsonファイルになってるモデルデータは、tinywavefrontobj.rb を使って用意した。
 - 頂点シェーダのソースファイル(.vert)と、フラグメントシェーダのソースファイル(.frag)は、前述の参考記事内で紹介されてるものを利用させてもらって実験。
 
結果画面は、こんな感じに。
ということで、Ruby + gosu + opengl を使っても、OpenGL 2.0風の書き方はできるみたいだなと。
◎ 課題。 :
今回、マテリアル(材質設定)を一種類に決め打ちして描画したけれど。複数のマテリアルを使ったモデルデータを描画したい場合は、どういう書き方をすればいいのかなと…。
_glBegin と glEnd を使って描画した場合 は、処理が遅い代わりに、ポリゴン一枚ごとに材質設定をし直すこともできたので、複数のマテリアルを使っていても描画ができたけど。頂点インデックス配列を渡して一気に描画する場合は…。
もしかしてそのあたり、マテリアル毎に分割した頂点インデックス配列を用意して対応することになるのだろうか。それとも、「この面にはこのマテリアルを使う」てな指定も可能になる書き方があったりするのだろうか。マテリアル情報をVRAMに転送しておいて、マテリアルインデックス配列を渡して、みたいな。…そんなことできるのかな。
テクスチャを使ってる時と使ってない時で、違うシェーダを使って処理してるあたりも気になる。1つのシェーダでどちらにも対応できないものか…。
_glBegin と glEnd を使って描画した場合 は、処理が遅い代わりに、ポリゴン一枚ごとに材質設定をし直すこともできたので、複数のマテリアルを使っていても描画ができたけど。頂点インデックス配列を渡して一気に描画する場合は…。
もしかしてそのあたり、マテリアル毎に分割した頂点インデックス配列を用意して対応することになるのだろうか。それとも、「この面にはこのマテリアルを使う」てな指定も可能になる書き方があったりするのだろうか。マテリアル情報をVRAMに転送しておいて、マテリアルインデックス配列を渡して、みたいな。…そんなことできるのかな。
テクスチャを使ってる時と使ってない時で、違うシェーダを使って処理してるあたりも気になる。1つのシェーダでどちらにも対応できないものか…。
◎ 参考ページ。 :
_Ruby/Qt/OpenGL で GLSL を使ってみる - tuedaの日記
_床井研究室 - 第1回 シェーダプログラムの読み込み
_床井研究室 - 第2回 Gouraud シェーディングと Phong シェーディング
_床井研究室 - 第3回 テクスチャの参照
_算譜記録帳: OpenGLでの頂点データの扱いの変化
_OpenGLプログラミング - Wikibooks
_OpenGLプログラミング/モダンOpenGL イントロダクション - Wikibooks
_OpenGLプログラミング/モダンOpenGL チュートリアル 02 - Wikibooks
_OpenGLプログラミング/モダンOpenGL チュートリアル 03 - Wikibooks
_GLSL編01 - OpenGL de プログラミング
_床井研究室 - 第1回 シェーダプログラムの読み込み
_床井研究室 - 第2回 Gouraud シェーディングと Phong シェーディング
_床井研究室 - 第3回 テクスチャの参照
_算譜記録帳: OpenGLでの頂点データの扱いの変化
_OpenGLプログラミング - Wikibooks
_OpenGLプログラミング/モダンOpenGL イントロダクション - Wikibooks
_OpenGLプログラミング/モダンOpenGL チュートリアル 02 - Wikibooks
_OpenGLプログラミング/モダンOpenGL チュートリアル 03 - Wikibooks
_GLSL編01 - OpenGL de プログラミング
[   ツッコむ ]
以上です。
