あかすくぱるふぇ

同人サークル「あかすくぱるふぇ」のブログです。

OpenGL

glVertex()の代わりとしてglVertexAttrib()、glVertexPointer()の代わりとしてglVertexAttribPointer()が存在します。
これらの関数の存在意義は何か。
それはGLSL(OpenGL)のバージョンと大きく関係しています。

新しいバージョン(GLSL1.5くらいから?)では、gl_Vertexなどの自動で設定されるAttribute変数が廃止となっており、アプリケーションプログラムからglVertex()を呼んだだけではシェーダーによる描画ができなくなっています。
そこで必要となるのがAttribute変数の明示的な設定であり、それを行うのが前述の~Attrib()です。
Attribute変数の明示的設定の手順は以下の通りです。

1. glGetAttribLocation()で、Attribute変数のインデックス(アドレスみたいなもの?)を取得。
  →GLuint appPos = glGetAttribLocation(program, "vertexPos");
2. gEnableVertexAttribArray(appPos)で、Attribute変数を有効にする。
3. glVertexAttrib()で、頂点情報を設定する。
4. glDrawArrays()で描画。

なお、GLSLのバージョン指定は、シェーダープログラムの最初に、#version 120などと書くことによって行います。
この指定をしないとPCに入っているOpenGLのバージョンなどによって挙動が変わってしまいますので、ちゃんと指定した方がよいと思われます。

参考サイト
http://mklearning.blogspot.jp/2014/08/opengl.html
http://wlog.flatlib.jp/item/1633
http://www.arakin.dyndns.org/glsl_qualifier.php

バッファオブジェクトやシェーダなどOpenGL拡張機能を使う場合、GLEWを利用するのが一般的かと思います。
ただ、GLEWなしでOpenGL拡張機能を使えるようにすることは可能ですし、それほど手間もかかりません。
余計なライブラリを使いたくない場合などは試してみるとよいかと思います。

まず、使い方の例から。
無題
1. glext.hをインクルード
2. 使いたい拡張関数に対して、PFN”関数名"PROC型の変数を宣言
3. wglGetProcAddress()
4. 拡張関数を使う

・glext.h
OpenGLの拡張機能が宣言されているヘッダファイルです。
OpenGLの公式ページからダウンロードできる安心安全な代物です♪
GLEWなんていらなかったんや!
https://www.opengl.org/registry/

・PFN"関数名"PROCの意味
定義へ移動してみると分かりますが、これは関数ポインタの型です。
関数ポインタの説明は以下のページが分かりやすいと思います。
http://www7b.biglobe.ne.jp/~robe/cpphtml/html03/cpp03009.html
例えば、PFNGLGENFRAMEBUFFERSPROCは、引数が(GLsizei, GLuint), 返り値がvoidの関数ポインタの型ということです。
なお、APIENTRYPはエントリポイント(関数のアドレス)という意味なので、単に"*"と読み替えればよいです。

・wglGetProcAddress()
デバイスドライバから関数のアドレスを取得し、glGenFrameBuffers(という関数ポインタ型の変数)に代入します。
上記リンクにあるように、関数の呼び出しは"関数のアドレス()"で行うので、glGenFrameBuffers()と書くことで、拡張機能を呼び出すことができます。

「バッファをバインドするんだな」と、納得したフリをしていたけれど……。
具体的に何が起こっているのかはよく分からない。

glBindBuffer関数について深く考えるきっかけになったのは、VBOやPBOについて勉強してる時でした。
glBindBuffer関数の有無によって、glVertexPointer関数やglReadPixels関数の引数の意味が変わっている。
例えば、VBOなしのVertexArray(glBindBuffer関数なし)ではglVertexPointer関数の第四引数が配列の先頭ポインタなのに対して、VBOにおけるVertexArray(glBindBuffer関数あり)ではVBO内のオフセットに変わっている。
http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?OpenGL%20-%20VBO

これはいったいどういうことなんだろう。
そんな疑問を解決したいと思って色々考えてみました。

結論から言うと、glVertexPointer関数などの引数として与えるポインタは、「バインドされているバッファの先頭アドレスからのオフセット」であり、バッファがバインドされていない場合に特例として、アドレス0からのオフセットと解釈されるようです。
以下、解説していきます。

まず、glBindBuffer(target, bufferId)の引数から解説します。
targetはバッファの種類を表しています。
そして、glBindBuffer関数では、targetで指定したバッファ種類について「バッファとしてbufferIdのバッファを使ってね」と設定(バインド)するわけです。
以下では、具体例として、targetにGL_ARRAY_BUFFERとGL_PIXEL_PACK_BUFFERが指定された場合について説明します。

・GL_ARRAY_BUFFER(glVertexPointer, glDrawBuffer)
glVertexPointer関数で頂点配列の「バインドされているバッファの先頭からのオフセット」を指定し、glDrawBuffer関数で指定された頂点配列を描画します。
この時、バインドされているバッファがない--つまり、glBindBuffer(GL_ARRAY_BUFFER, 0)となっている場合は、アドレス0からのオフセットと解釈されます。
アドレス0からのオフセットってことはつまり、頂点配列の先頭アドレスを与えることになるわけです。

・GL_PIXEL_PACK_BUFFER(glReadPixels)
glReadPixels関数で格納先の「バインドされているバッファの先頭からのオフセット」を指定し、ピクセル情報を読み込みます。
この時、バッファがバインドされていれば、そのバッファに読み込みます。
一方、glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)となっている場合は、アドレス0からのオフセットを指定する必要があるので、格納先の先頭アドレスを指定することになるわけです。

OpenGLのディスプレイリスト、頂点配列、VBOを比較します。

まず、3手法に共通するのは、glVertexなどのAPI呼び出しを減らすことで処理を高速化している点です。
その上で、3手法には下表に示すような特徴があります。
無題
メモリ転送負荷は、CPU→GPUのメモリ転送にかかる時間を表しています。
データ変更可能性は、後から頂点データの変更が可能か否かを表しています。
両方に丸がついているVBOを使うのが良いと思われますが、一応全部説明します。

・ディスプレイリスト
glVertexなどの命令をGPU上のメモリにまとめて格納してから呼び出すことで、API呼び出しとメモリ転送の負荷を減らします。
ただし、後から頂点データの変更はできません。
http://seesaawiki.jp/w/mikk_ni3_92/d/%a5%c7%a5%a3%a5%b9%a5%d7%a5%ec%a5%a4%a5%ea%a5%b9%a5%c8

・頂点配列(Vertex Array)
頂点データを配列として用意し、1回のAPI呼び出しでまとめて描画することでAPI呼び出しの負荷を減らします。
後から頂点データを変更することができますが、描画の度にCPU→GPUのメモリ転送が発生します。
使い方は、
1. 頂点配列の用意
2. 配列の指定(glVertexPointer)
3. 配列の描画(glDrawArrays, glDrawElements)
http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?OpenGL%20-%20Vertex%20array

・VBO(Vertex Buffer Object)
上記頂点配列をGPU側のメモリにあるバッファに格納して処理を行う。描画の度にCPU→GPUのメモリ転送が発生しない。
使い方は、
1. 頂点配列の用意
2. バッファの生成(glGenBuffers)
3. バッファ転送(glBufferData)
4. 配列の指定(glVertexPointer)
5. 配列の描画(glDrawArrays, glDrawElements)
※glVertexPointerの引数の意味が、上記頂点配列とは異なるので注意。
http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?OpenGL%20-%20VBO


まず、OpenGL自体はフリーで使えます。
ライセンスは、SGI Free Software License B Version 2.0というもので、MITライセンスに基づくライセンスだそうです。
http://osdn.jp/magazine/08/09/21/2324233
http://osdn.jp/magazine/08/09/24/010239
http://kojika17.com/2011/01/web-designers-have-to-remember-license-summary.html

Visual StudioをインストールすればOpenGLの基本機能はすぐに使えますが、拡張機能はglext.hを入手しないと使えません。
glext.hはたまたまダウンロードしたプログラムなどに付属している場合がありますが、古い版だと新しい機能が漏れている場合があるので、以下のページから最新版を入手するが吉です。
https://www.opengl.org/registry/

OpenGLの拡張機能を使う場合、普通はGLEWというライブラリを使いますが、OpenGLとは異なるライセンスなので注意が必要(と言っても、修正BSDライセンスとMITライセンスなので緩いけど)。
余計なライブラリを使いたくない場合、GLEWがなくても、OpenGLの拡張機能は使えます。
http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20040212
http://project-asura.com/blog/?p=401

なお、「glext.hに対応するライブラリファイルは必要ないのか」という疑問を持つ方がいるかと思いますが、上記リンクにあるように、wglGetProcAddress()を用いて関数のアドレスを取得して利用するので、ライブラリは不要なようです。
https://shikihuiku.wordpress.com/2012/07/30/windows%E4%B8%8A%E3%81%A7%E3%81%AEopengl-extension/



↑このページのトップヘ