ラベル OpenGL の投稿を表示しています。 すべての投稿を表示
ラベル OpenGL の投稿を表示しています。 すべての投稿を表示

2010年6月1日火曜日

Cygwin+Opengl+Makefile

Check
ここんところこんなのばっかですね。SDLもいいけど、結局Windows用とX用にそれぞれべた書きしたほうがシンプルでいいんじゃないかという気がしてきた。今win32apiのほうのウィンドウを作成しているところです。

まずは、ビルドしたいソースファイル一式。

2010年5月18日火曜日

wxWidgets + OpenGL + Cygwin or Linux(Lenny) + Mercurial + Emacs + CEDET = 大混乱

Check
タイトルのとおり、学校では上記の環境で、家ではDebian(lenny)を使って開発していたら、覚えることが多すぎてブログを更新する暇すらなかった。本当はちょこちょこ覚えるたびに更新するのが理想なのだろうけど…

まずは、wxWidgetsによるOpenGLフレームの作成。現在のところ、三次元日誌さんの『wxWidgetsのOpenGL雛形』を丸写し。なさけないが、とりあえずはこれで必要なソースはできた。

次に、Cygwinでのビルド。コンパイラにいろいろなパラメタを与えなければいけないのだが、wx-configというコマンドを使うことでかなり楽をできる。
  • wx-config --cxxflags インクルードパスやコンパイルオプションなどを勝手にやってくれる
  • wx-config --libs gl コアライブラリやwxWidgetsのGL用ライブラリパスと名前の解決をやってくれる。GLが必要ないときは、glという引数を外せばよい。
実際にコマンドラインで使用するときは以下のようになる。
g++ main.cpp `wx-config --libs gl` `wx-config --cxxflags`
Lennyではこのままでもリンクまで通るのだが、Cygwinでやるとリンクエラーをおこしてしまう。libopengl32.lib.aを見つけきれないのが原因だと思われるので、上記のオプションに加え、-lopengl32を加えてやると無事通った。
g++ main.cpp `wx-config --libs gl` `wx-config --cxxflags` -lopengl32
Lenny上での実行イメージは以下のとおり。


さて、こういう風に作ったコードをリポジトリに保存して管理も一元化しようと思いたくなるのは人の常。くわえて、今学校の先生と一緒にいろいろ企んでいるので、他の人からも参照出来るようにしたい。

cvsやsvnあたりは定番だが、ディレクトリごとに管理用のファイルを作られてしまう上に、GitやMercurialといった分散型と比べるとちょっと古くさい気もしたので、今回はMercurialを選択することにした。Mercurialの何が良いって、デフォルトでhttpサーバとして機能するところ。ちょっと前まで、おうちのマシンでMercurialサーバーを動かしていたのですが、httpサーバーだとよほどのことがない限りwebフィルタリングにも引っかかりません。惜しむらくは、cvsやsvnはもとより、Gitと比較しても普及していないところでしょうか。もっと流行ってほしいな。

bitbucketというMercurial用のリポジトリサービスも発見したので、今はここを使わせてもらっています。

Mercurialの超簡単な操作説明。当然コマンドラインからです。Linuxはもちろんのこと、Windows、Cygwinと多くのプラットフォームから使用できます。今のところ私が使ったものだけを紹介。hgというのがMercurialのコマンド名。水銀のことだそうです。
  • hg init リポジトリをカレントディレクトリに作成。
  • hg clone http://foo.bar/ リモートにあるリポジトリをカレントディレクトリにコピー。
  • hg add filename filenameをローカルリポジトリに追加。
  • hg commit 変更をローカルリポジトリにコミットする。この時、オプションである-u usernameでコミットしたユーザを、-m "文字列"でエディタを開かずにメッセージを追加できます。
  • hg remove filename filenameを削除ののち、次回のコミット時にリポジトリから削除。
  • hg forget filename filenameをローカルリポジトリから外す。ファイル自体は消さない。
  • hg rename from to ファイル名をfromからtoに変更する。
  • hg push ローカルリポジトリをリモートにあるリポジトリへ送る。
  • hg pull リモートリポジトリからローカルリポジトリへコピー。 -uをpullの後ろにつけることで、即時にローカルリポジトリを更新。hg pull -uで1セットという感じ。他の使い方は知らん。
ざっとこんな感じ。設定ファイルは.hg/hgrcあたりをいじるのだが、正直コミット時のオプションで何とかなっているのでほとんど触ってない。

2010年5月7日金曜日

CygwinでGLWindow

Check
ようやくCygwin+EmacsでOpenGL開発ができるようになってきた。

makefileが曲者で、Cygwin上で動かすためにはリンク時に-mno-cygwinというオプションが必要だということがわかった。
#makefile

VPATH = /usr/include/opengl
CPPFLAGS = -I /usr/include/opengl

libss := -lglut32 -lglu32 -lopengl32

main: main.o
main.o: main.cpp

%.o: %.cpp
$(COMPILE.cpp) $(OUTPUT_OPTION) $<

%: %.o
$(LINK.o) $^ -mno-cygwin $(libss) -o $@

.PHONY: clean
clean:
rm -f *.o *.exe
$(LINK.o)の行にある-mno-cygwinというオプションを指定することでリンカが-lglut32のリンク先を見つけてくれるようになります。これがないと-lglut32が見つかりませんというメッセージが出てリンクエラーとなる。

makefileについては調べながら勉強中みたいなものなので、ひどい書き方になってるかもしれませんがご容赦ください:-)

以下はソースコード。

#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>a

const char *APP_NAME = "GL Window on Emacs!!";

void Display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glutSwapBuffers();
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(500, 500);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);

    glutCreateWindow(APP_NAME);
    glutDisplayFunc(Display);

    glClearColor(1,1,1,0);
    glutMainLoop();

    return 0;
}
実行例

2010年4月20日火曜日

OpenGLでテクスチャ表示

Check
楽勝だと思っていたが意外と苦労した。アラインメントや配列について学びなおすいい機会だったと思う。とりあえず、テクスチャを表示するだけコードを載せてみよう。

#include <gl/glut.h>

//頂点座標
float vertices[][2] =
{
   {-0.5f,  0.5f},
   {-0.5f, -0.5f},
   {0.5f, -0.5f},
   {0.5f,  0.5f},
};

//uv(st)座標
float uvs[][2] =
{
   {0.f, 1.f},
   {0.f, 0.f},
   {1.f, 0.f},
   {1.f, 1.f},
};

//テクスチャハンドル
GLuint hTextures[1];
const unsigned TEXSIZE = 64;

//画素バッファ
GLubyte texImage[TEXSIZE][TEXSIZE][4];

//アイドル時にも画面は更新する
void Idle()
{
   glutPostRedisplay();
}

//描画関数
void Display()
{
   glClear(GL_COLOR_BUFFER_BIT);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, hTextures[0]);

   //反時計回りに板ポリを描画
   glBegin(GL_QUADS);
   {
      glVertex2fv(vertices[0]); glTexCoord2fv(uvs[0]);
      glVertex2fv(vertices[1]); glTexCoord2fv(uvs[1]);
      glVertex2fv(vertices[2]); glTexCoord2fv(uvs[2]);
      glVertex2fv(vertices[3]); glTexCoord2fv(uvs[3]);
   }

   glEnd();
   glBindTexture(GL_TEXTURE_2D, 0);

   glDisable(GL_TEXTURE_2D);
   glutSwapBuffers();
}

//テクスチャ初期化
void InitTexture()
{
   //テクスチャを1枚生成する
   glGenTextures(1, hTextures);

   //今回は計算でテクスチャを作る
   for(unsigned y=0; y<TEXSIZE; ++y)
   {
      int yy = y / 8;
      for(unsigned x=0; x<TEXSIZE; ++x)
      {
         int xx = x / 8;
         int col = ((yy+xx)&1)*255;

         texImage[y][x][0] = col; //r
         texImage[y][x][1] = 0;   //g
         texImage[y][x][2] = ~col;//b
         texImage[y][x][3] = 0xff;//a
      }
   }

   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

   //hTexures[0]の設定をここから行うという宣言
   glBindTexture(GL_TEXTURE_2D, hTextures[0]);

   //VRAMにイメージを転送
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXSIZE, TEXSIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);

   //テクスチャマッピング方式。今回は繰り返しなし
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

   //補完フィルタ設定。市松模様なのでnearestでおk
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

   //ポリゴンカラーとの混色設定
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

   //hTexures[0]の設定終了
   glBindTexture(GL_TEXTURE_2D, 0);
}

//エントリポイント
int main(int argc, char **argv)
{
   //お決まりの処理
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_DOUBLE| GLUT_RGBA);
   glutCreateWindow("DrawTexture");
   glutDisplayFunc(Display);
   glutIdleFunc(Idle);

   glClearColor(0,0,0,1);
   InitTexture();

   //メインループに入る
   glutMainLoop();

   //終了処理
   glDeleteTextures(1, hTextures);
   return 0;
}
テクスチャを表示するだけでこの面倒さ。実際にはBMPなどの画像を読み込む処理も要りますから、これからが大変ですな・・・。あと、3次元配列を最初から用意しておくのもどうかと思うので、こいつをクラス化したりもしました。この辺はまたあとで。

2010年4月15日木曜日

OpenGL+GLSLでポイントライトと距離減衰

Check
距離によって強さが変わるライトの実装に成功した。

ある地点における光の強さは、光源までの距離を d 、一定減衰率を kc 、1時減衰率を k1 、2時減衰率を k2 とすると以下の式で表すことができます。
減衰率kc, k1, k2はそれぞれGL側(ホストプログラム)からシェーダ側に送ることができる。

以下はホスト側のプログラム
//距離減衰
//kc: 一定減衰率 = 0
//k1: 1次減衰率 = 0
//k2: 2次減衰率 = 1.0 / (lightPos.y * lightPos.y)
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, kc);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, k1);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, k2);
んで、フラグメントシェーダ側の記述は以下のとおり
varying vec3 P;
varying vec3 N;

void main()
{
    //法線やライティング情報を受け取る
    vec3 L = gl_LightSource[0].position.xyz - P;

    //ライトまでの距離
    float d = length(L);

    //ライトへの向きベクトルを正規化する
    L = normalize(L);

    //減衰係数
    float attenuation = 1.0 / (gl_LightSource[0].constantAttenuation
                       + gl_LightSource[0].linearAttenuation * d
                       + gl_LightSource[0].quadraticAttenuation * d * d);

    vec3 N = normalize(N);

    //アンビエント確定
    vec4 ambient = gl_FrontLightProduct[0].ambient;
    float dotNL = dot(N, L);

    //ディフューズ確定
    vec4 diffuse = gl_FrontLightProduct[0].diffuse * max(0.0, dotNL);

    vec3 V = normalize(-P);
    vec3 H = normalize(L + V);
    float powNH = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess);

    if(dotNL <= 0.0)
    {
        powNH = 0.0;
    }
    //スペキュラ確定
    vec4 specular = gl_FrontLightProduct[0].specular * powNH;

    //カラー値統合
    gl_FragColor = ambient + diffuse + specular;

    //減衰係数を乗じる
    gl_FragColor *= attenuation;
}
まあほとんど教本のまんまなんですが・・・。
gl_LightSource[0].constantAttenuation
gl_LightSource[0].linearAttenuation
gl_LightSource[0].quadraticAttenuation
がそれぞれホスト側で設定した減衰係数です。

画像の式に当たるのがコメントで減衰係数と書いてあるところ。んで、最後にカラー値を統合したgl_FragColorに減衰係数をかけて明るさを確定してるわけですね。このあたりはレイトレースでやったことがかなり活きてるかんじ。

OpenGLで簡易シャドウ

Check
OpenGLでのお話。今まで敬遠してきたシャドウイングにチャレンジ。簡易シャドウというらしい。原理はまだよくわからないが、使用している行列や平面のパラメトリック表現から察するに、ライトの位置から平面にびたーっと張り付くように頂点変形をさせるのが影の変換行列ってやつらしい。
影の変換行列
平面の方程式を ax + by + cz + d = 0 、光源ベクトルをe(ex, ey, ez) とすると、影の変換行列(Ms)は
となります。行列の意味についていろいろ考えたいが、とりあえず後回し。

しかし、よくよく見るとポリゴンが重なってる部分は影が濃く出てますね。自分の影を見ればわかりますが、透明なガラスならともかく、この影は不自然ですね。おそらくこれが「簡易」シャドウといわれる所以でしょう。

上記のマトリクスを最初はHTMLに書き込もうと思ったが、画像化してくれる超便利なツールがオンラインで使用できるのでそちらを使った。LATEX形式だが、知らなくても適当にボタンを押してるだけでそれっぽく仕上がるナイスな出来!