2009年04月18日

表示部分のコアを考える。(xlibでOpenGL)

クリエーターが使いやすいゲーム実行環境(Flash player)を作り
自分もそれにのっかってシナリオベースでゲームを作るということが今の作業なのだが
コアのWindowシステムをどうするか描画システムをどうするかということについて
どうも決めかねている。
今回はSDLとglut以外の選択肢として
あえて一般的でもクロスプラットフォームでもないxlibでOpenGLを使ってみた。
昨年今頃は「後方互換性をもたせるため固定パイプラインでDirect3D9でいく」
としてちょっとずつすすめていたのを打ちきったわけだが
今回は「OpenGLで将来を見据えてOpenGL-2.0メインでいく」ことにした。
OpenGLの場合マトリクス関数はデフォルトでついてくるが
描画用変形計算はGPUでやる感じになるのだろう。
gpuもアセンブラには手を出さずGLSLのみでいく。

ただし頭の中が10年前のままなので今回はとりあえずglbegenとglendで一枚四角形をだすだけにする。

今回のコードに至るに当たってcairoのバックエンドとされるglitzを調べたのだが
資料が少ないし何をやっているか見えない。プロジェクト自体胡散霧消しているみたいなので
とりあえずcairoはテクスチャー生成と印刷にのみ使うことにして
cairoそのものまで組むぐらいの感じでいこうと思う。

OpenGL2.0でいくというからにはGLSLなのだが
幸いにも現在使用のFX6200はGLSLがそこそこ機能してくれるようだ。

現状内部描画やプリンタ出力用にcairoをライブラリーとして確保したが
根幹の選択肢としてSDL、GLUT、GTK、xlibとあるのだが
SDL or xlibですすめようと思う。今回はxlib

OpenGLは基本的にIncludeパスには含ませないのが慣例のようだ。

コンパイル時のコード

gcc -O2 -pipe -I/usr/local/include \
-L/usr/local/lib -lX11 -lGL -lGLU \
-o glx01 glx01.c


ヘッダー宣言
xlibはglxの宣言に含まれる。

#include<stdio.h>
#include<stdlib.h>
#include<GL/gl.h>
#include<GL/glx.h>
#include<GL/glu.h>
#include<GL/glext.h>
#define WIN_TITLE "GLX_01"

入れたいからglextを入れたが今回は使っていない。
blenderについてきたglewを使うと自動でイニシャライズしてくれるらしい。


void Drawfunc() {
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1., 1., -1., 1., 1., 20.);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0., 0., 10., 0., 0., 0., 0., 1., 0.);

glBegin(GL_QUADS);
glColor3f(0., 0., 0.); glVertex3f(-1.0, -1.0, 0.);
glColor3f(0., 1., 1.); glVertex3f( 1.0, -1.0, 0.);
glColor3f(0., 1., 1.); glVertex3f( 1.0, 1.0, 0.);
glColor3f(1., 1., 1.); glVertex3f(-1.0, 1.0, 0.);
glEnd();
}

クラシックなコードgluはこのgluLookAtのみで使っている。
こうやってみると引数が全くなくぶんなげで描画できるが
いいかえればコンテキストの切り替えが出来ない。
これを実行すると左下が黒く左上が白くなる。
左下がマイナス方向で左回りということだ。
四角描画命令だが左下から右上に向かって濃い色の線がでる。
このやり方だと2Dのアニメーションを表示できる。

昨年の復習をするならばここいらは自作できるわけだが
vertex shaderに任せるのが正とするなら半端な事はしない方が良いだろう。
直接glBegenとglEndの組みで描画するのも最低のやり方で
本筋はバッファオブジェクトを使うのだが旧来の手法でも
一旦宣言しておいて呼び出すのが正しい。

メイン関数宣言と変数ブロック

int main(int argc, char **argv) {

//var
Display *dpy;
Window root,win;
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;

XWindowAttributes gwa;
XEvent evt;
Atom atom_wm;
KeySym key_sym;
int do_loop = 1;

GLXContext glc;
GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };


main関数を *argv[]と書くか、**argvと書くかいまだに決めかねている。
XVisualInfo、GLXContextとアトリビュート配列が普通のxlibサンプルとの違いで
目新しい。
変数はできるだけ最初に宣言する様にしているが
途中で宣言しないと無駄にスタックフレームを確保するなら方針は変更した方がいいかなと思っている。


dpy = XOpenDisplay(NULL);
root = DefaultRootWindow(dpy);

vi = glXChooseVisual(dpy, 0, att);
if(vi == NULL) {
printf("\nGL visual not found\n\n");
exit(0);
}
else {
printf("\nvisual ID:%p \n", vi->visualid);
}
cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);

ルートを取得してglxchooseVisual関数を呼ぶ
これはwinだとchoosepixelになるところ。

Window生成部分 XInternAtomは以前学んだウィンドメニューでウィンドが閉じた時対策


swa.colormap = cmap;
swa.event_mask = ExposureMask | KeyPressMask| ButtonPressMask;

win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
atom_wm = XInternAtom( dpy, "WM_DELETE_WINDOW", True );
XSetWMProtocols( dpy, win, &atom_wm, 1 );

XMapWindow(dpy, win);
XStoreName(dpy, win, WIN_TITLE);

glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
glXMakeCurrent(dpy, win, glc);




glEnable(GL_DEPTH_TEST);
printf ("OpenGL version: %s\n", glGetString (GL_VERSION));
printf ("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
printf ("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
printf ("OpenGL extentions: %s\n", glGetString (GL_EXTENSIONS));


今回の場合はDEPTH_TESTはいらないだろう。
とりあえず理解している4つのstringを取得してみた。
OpenGLアプリではよくおめにかかる。


do_loop = 1;
while(do_loop) {
XNextEvent(dpy, &evt);
switch( evt.type ) {

case Expose:
XGetWindowAttributes(dpy, win, &gwa);
glViewport(0, 0, gwa.width, gwa.height);

Drawfunc();
glXSwapBuffers(dpy, win);

// if( evt.xexpose.count == 0 ) {}
break;
//
case ButtonPress:
puts("button");
if (selected == False) {
if (evt.xbutton.button == Button1) {
puts("button 1");
break;
}
else {
puts("button other");

}
}
break;

case KeyPress:
XLookupString( &( evt.xkey ),
NULL, 0, &key_sym, NULL );

switch ( key_sym ) {
case XK_Left:

puts("key left");
break;

case XK_Right:
puts("key right");
//system("espeak -v f2 -p 150 -s 120 'right'&");
break;

case XK_Up:
puts("key up");
break;

case XK_Down:
puts("key down");

break;

case XK_Escape:
do_loop = 0;
break;
}
break;

case ClientMessage :
if ( evt.xclient.data.l[0] == atom_wm ) {
//close win
do_loop = 0;
}
break;
}
}
glXMakeCurrent(dpy, None, NULL);
glXDestroyContext(dpy, glc);
XDestroyWindow( dpy, win );
XCloseDisplay( dpy );
return(0);
}


ガリガリと描くんじゃなくて要求のあったときだけ描いている。
exposeイベントはresizeの時にも起きるようで
resizeしないならばglXSwapBuffersだけでもよい。
開放時にglXDestroyContextが必要なのがベーシックなxlibアプリとの違い

ほんとはglitzもあれこれやってみて挫折したのだが
マクロかinclude化すればxlibのべたプログラムも悪くない。
OpenGLアプリは描画用演算にGPUを使えるので低リソースになるかと思ったら
今回みたいな単純なものでも使用メモリは18Mbyteくらいあってrsvg2+SDL+cairoを上回る。
余計なライブラリーを全部読み込んでいるせいだと思うけれど
非GL部分の確保とリソースを抑えることは考えた方がいいだろう。
glに手をだし始めたので昨年より庶民的かつ見栄えの良いサンプルもやっていこうと思うが
スレッドとかツリーとかストリームとかswfのパースとか問題は山積しているので
ちょっとずつ片付けたい。


ラベル:opengl xlib
posted by Xo_ox at 23:45| Comment(0) | FreeBSDアプリ | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。