みけCATのにっき(仮)
つれづれなるまゝに、日くらし、PCにむかひて、心に移りゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。
(本当か!?)
出典

こいつ(OpenGLのシェーダー)…動くぞ!

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

こいつ(OpenGLのシェーダー)…動くぞ!

投稿記事 by みけCAT » 11年前

前に自分の環境で動作させることができなくて、一旦諦めていたOpenGLのシェーダーですが、
このたび、無事(基本的な)動作をさせることができました!
これで、やっとスタート地点に立てました!

※今回の実験では、いろいろ「普段の」開発では考えないようなことを考え、実行しないと詰んでいます。
したがって、今回の結果は自分の環境固有であり、他の環境では同じ手順を使用しても成功しない、
という可能性が考えられます。

今回の検証環境
Windows Vista Home Premium SP2 32ビット
Intel(R) Core(TM)2Duo T8100 @2.10GHz 2.10GHz
RAM 4.00GB
gcc 4.8.1 (Dev-C++ 5.6.1付属のMinGW)

1. 昨日の実験

1.1. とりあえず動かす

OpenGL + GLSL(GLEW)を用いた開発環境の設定方法 (WebArcive)
ここに書いてある手順を参考にしながら環境の構築を試みた。

1. これは手順ではない。
2. 省略。といっても既にCUDA用のVS2008がインストールされているので、本当にインストール不要かは不明。
3. グラフィックドライバは最新であった。
4. 指定のサイトからglut-3.7.6-bin.zipをDLし、適当なディレクトリに解凍する。(指示があるコピー・設定は行わない)
5. 指定のサイトからglew-1.10.0-win32.zipをDLし、適当なディレクトリに解凍する。(指示があるコピー・設定は行わない)

そして、
床井研究室 - 第1回 シェーダプログラムの読み込み (WebArchive)
のglsl1.zipをDLして適当なディレクトリに解凍した。
結論から言うと、Makefileの最初の2行を

CODE:

CXXFLAGS	= -O2 -I../glut-3.7.6-bin -Wall
LDLIBS	= -static -s -L../glut-3.7.6-bin -lglut32 -lglu32 -lopengl32 -lm
(../glut-3.7.6-binは前述のGLUTがあるディレクトリ)
と書き換えればコンパイルが通り、さらに生成されたexeと同じディレクトリにglut32.dllをコピーすることで実行はできた。
(このプログラムのコンパイルにGLEWは必要なかったようだ)
しかし、このままだと「Version number not supported by GL2」と怒られてしまった。
simple.vertおよびsimple.fragの先頭の「#version 120」という行に原因があると考え、数字を書き換えてみたが、
結局単純にこの行を削除(コメントアウト)すれば実行でき、グラフィックが表示されることがわかった。

1.2. シェーダーをいじってみる

これでOpenGLのシェーダーを動かすことに成功したので、ちょっとだけシェーダーをいじってみることにした。
どうやら、OpenGLのシェーダーは「頂点シェーダー」と「フラグメントシェーダー」の2段階からなり、
「頂点シェーダー」はglVertexHogeの座標を描画座標に変換し、
「フラグメントシェーダー」が実際に描画する色を決めているようだ。
また、「頂点シェーダー」は「フラグメントシェーダー」に渡す色情報を制御することもできるようだ。

このことを踏まえ、シェーダーで画像を生成することを考えたシェーダープログラムを書いてみた。
頂点シェーダー

CODE:

void main(void)
{
  gl_Position = gl_Vertex;
  gl_FrontColor = vec4(gl_Vertex.x/2.0+0.5, gl_Vertex.y/2.0+0.5, 0.0, 0.0);
}
描画位置はそのまま渡し、色情報として座標を渡す。

フラグメントシェーダー

CODE:

void main (void)
{
  gl_FragColor = gl_Color;
}
渡された色をそのまま返す。

実行結果は後述のプログラムとほぼ同じなので、ここでは省略する。

2. 今日の実験

昨日の実験でとりあえずOpenGLのプログラムを動かすことに成功したが、
(ライブラリとして公開されているわけではない)他人のプログラムに依存している。
シェーダーだけ配布するならいいが、実行可能なバイナリを配布する際に著作権の問題が発生しそうなので、
できれば依存を切りたい。

試行錯誤の結果、以下の方法で床井研究室のプログラムを用いずにOpenGLのシェーダーを動かせることがわかった。
1. http://glew.sourceforge.net/ から、一番上のSource TGZをダウンロードする。
この時、バイナリをダウンロードすると詰む。Source ZIPでも大丈夫かは試していない。
2. ダウンロードしたソースコードを解凍し、普通にmakeコマンドを用いてビルドする。
README.txtには「Windowsならbuild/vc6/のプロジェクトを使え」と書いてあるが、
バカ正直にこれに従うと詰んだ。
また、makeすると「undefined reference to `__mingw_glob'」というやばそうなエラーが出たが、
必要なDLLとインポートライブラリはできていたので無視する。
3. http://user.xmission.com/~nate/glut.html からGLUTのバイナリをダウンロードする。
ソースのビルドは試みたが、詰んだ。
4. dllからインポートライブラリを作成する方法を用いて、
ダウンロードしたバイナリに含まれるglut32.dllとglut.defからインポートライブラリlibglut32.aを作成する。
ただし、--dll-nameのかわりに--dllnameと入力しないといけなかった。
バイナリに含まれるlibファイルをそのまま用いると詰んだ。
5. 以下のプログラムとMakefileを用意する。

glsl_test.cpp

CODE:

#include 
#include 
#include 
// わざわざglut.h内でwindows.hを避けると明言してあるのに、
// glut.hの前でwindows.hをインクルードしてしまうと死ぬので注意する
#include 

static const GLchar* vert_program=
// おまじない
"void main(void)\n"
"{\n"
  // 無駄なことはせず、座標はそのまま渡す
  "gl_Position = gl_Vertex;\n"
  // 座標をフラグメントシェーダーに色情報として渡す。左下が(0,0)、右上が(1,1)
  "gl_FrontColor = vec4(gl_Vertex.x/2.0+0.5, gl_Vertex.y/2.0+0.5, 0.0, 0.0);\n"
"}\n"
;

static const GLchar* frag_program=
// おまじない
"void main (void)\n"
"{\n"
  // とりあえず、色情報をそのまま渡す
  "gl_FragColor = gl_Color;\n"
"}\n"
;

// OpenGLの描画関数
void drawFunc(void) {
	// 画面を初期化するおまじない
	glClear(GL_COLOR_BUFFER_BIT);
	// 画面いっぱいに四角形を描画する
	glBegin(GL_QUADS);
	glVertex2d(-1.0, 1.0);
	glVertex2d(-1.0,-1.0);
	glVertex2d( 1.0,-1.0);
	glVertex2d( 1.0, 1.0);
	glEnd();
	// 命令を確実に実行させる
	glFlush();
}

int main(int argc,char* argv[]) {
	GLuint vs,fs,pg;
	GLsizei length;
	GLint ret;
	GLenum err;
	// OpenGLの初期化
	glutInit(&argc, argv);
	glutInitWindowSize(256,256);
	// GLUT_SINGLEをGLUT_DOUBLEにするとウィンドウ内の表示がおかしくなった
	glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
	// glewInitを呼び出す前にこの関数でウィンドウを作っておかないとglewInitが失敗する
	glutCreateWindow(argv[0]);

	// シェーダーを使うライブラリの初期化
	err=glewInit();
	if(err!=GLEW_OK) {
		MessageBoxA(NULL,(const char*)glewGetErrorString(err),
			"glewInit falied",MB_OK | MB_ICONSTOP | MB_TOPMOST);
		return 1;
	}
	// シェーダーを読み込むおまじない (TODO: ファイルから読み込めるようにする)
	vs=glCreateShader(GL_VERTEX_SHADER);
	fs=glCreateShader(GL_FRAGMENT_SHADER);
	// 頂点シェーダーの読み込み
	// glVertexHogeの設定を実際の描画座標に変換し、ついでに色情報を設定する役割っぽい
	length=lstrlenA((const char*)vert_program);
	glShaderSource(vs,1,&vert_program,&length);
	glCompileShader(vs);
	glGetShaderiv(vs,GL_COMPILE_STATUS,&ret);
	if(ret==GL_FALSE) {
		GLsizei size;
		GLchar* info;
		glGetShaderiv(vs,GL_INFO_LOG_LENGTH,&size);
		info=new GLchar[size];
		glGetShaderInfoLog(vs,size,&size,info);
		MessageBoxA(NULL,(char*)info,
			"vert compile error",MB_OK | MB_ICONSTOP | MB_TOPMOST);
		delete[] info;
		return 1;
	}
	// フラグメントシェーダーの読み込み
	// 実際に描画する色を決める。重要そう
	length=lstrlenA((const char*)frag_program);
	glShaderSource(fs,1,&frag_program,&length);
	glCompileShader(fs);
	glGetShaderiv(fs,GL_COMPILE_STATUS,&ret);
	if(ret==GL_FALSE) {
		GLsizei size;
		GLchar* info;
		glGetShaderiv(fs,GL_INFO_LOG_LENGTH,&size);
		info=new GLchar[size];
		glGetShaderInfoLog(fs,size,&size,info);
		MessageBoxA(NULL,(char*)info,
			"frag compile error",MB_OK | MB_ICONSTOP | MB_TOPMOST);
		delete[] info;
		return 1;
	}

	// シェーダーを組み合わせてプログラムにするおまじない
	pg=glCreateProgram();
	glAttachShader(pg,vs);
	glAttachShader(pg,fs);
	glDeleteShader(vs);
	glDeleteShader(fs);
	glLinkProgram(pg);
	glGetProgramiv(pg,GL_LINK_STATUS,&ret);
	if(ret==GL_FALSE) {
		GLsizei size;
		GLchar* info;
		glGetProgramiv(pg,GL_INFO_LOG_LENGTH,&size);
		info=new GLchar[size];
		glGetProgramInfoLog(pg,size,&size,info);
		MessageBoxA(NULL,(char*)info,
			"program link error",MB_OK | MB_ICONSTOP | MB_TOPMOST);
		delete[] info;
		return 1;
	}

	// OpenGLでシェーダーのプログラムを使用する設定にする
	glUseProgram(pg);
	// 描画関数をOpenGLに登録する
	// なぜかglewInitより前に呼び出すとglShaderSourceでアクセス違反になる
	glutDisplayFunc(drawFunc);
	// 処理を開始する
	glutMainLoop();
	return 0;
}
Makefile

CODE:

CXXFLAGS = -Wall -I../glew-1.10.0/include -I../glut-3.7.6-bin
LDFLAGS = -s -mwindows -L../glew-1.10.0/lib -L../glut-lib
LIBS = -lglut32 -lglew32 -lopengl32
CXX = g++
RM = rm -rf

glsl_test.exe: glsl_test.o
	$(CXX) $(LDFLAGS) -o glsl_test.exe glsl_test.o $(LIBS)

glsl_test.o: glsl_test.cpp
	$(CXX) $(CXXFLAGS) -c -o glsl_test.o glsl_test.cpp

.PHONY: clean
clean:
	$(RM) glsl_test.exe glsl_test.o
このMakefileにおいて、
・CXXFLAGSの最初に-O2を追加すると、実行時に強制終了する
・LDFLAGSの最初に-staticを追加すると、glew関係と思われるundefined referenceが出てリンク失敗する
という罠がある。

6. makeする。
7. 生成されたexeファイルと同じディレクトリにglew32.dllとglut32.dllをコピーする。
8. exeを実行する。
9. いい感じのグラフィックが表示される。
glsl_test.png
こいつ…動くぞ!
glsl_test.png (8.04 KiB) 閲覧数: 253 回
これで、見事床井研究室のコードに依存せず、OpenGLのシェーダーを実行することができました。
まだまだ謎も多いですが、やっとスタート地点に立てました。

しかし、Dependency Walkerで確認すると、libgcc_s_dw2-1.dllとlibstdc++-6.dllに依存してしまっています。
これでは、まだバイナリ配布には向かないようです。
glsl_test_dw.png
Dependency Walkerで確認した結果
glsl_test_dw.png (45.96 KiB) 閲覧数: 243 回
3. 参考にしたサイトまとめ
OpenGL + GLSL(GLEW)を用いた開発環境の設定方法
床井研究室 - 第1回 シェーダプログラムの読み込み
GLEW: The OpenGL Extension Wrangler Library
Nate Robins - OpenGL - GLUT for Win32
dllからインポートライブラリを作成する方法
OpenGL入門
OpenGL 4 Reference Pages

コメントはまだありません。