----
#contents
----

*GLSL [#ga5e9b27]
OpenGL Shading Language

*GLSLプログラミング [#if3e7c73]
GLSLプログラミングを行うためには実行プラットフォームが少なくとも,
GL_ARB_vertex_shader,GL_ARB_fragment_shader拡張に対応している必要がある.
OpenGL拡張の対応を調べたり,拡張を使えるようにするにはGLEWを用いるのが一番簡単である
(参照:[[GLEWについて]]).

GLSLシェーダはいくつかのOpenGL命令を用いてアプリケーション実行時にロード,コンパイル,リンクされる.

***シェーダのコンパイル [#w4b7f675]
GLSLシェーダをOpenGLアプリケーション中でロード,コンパイルする手順は以下.
+glCreateShader()でシェーダオブジェクトを生成する.
 GLuint glCreateShader(GLenum shaderType);
shaderTypeにはGL_VERTEX_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADERのいずれかを指定する.
+シェーダオブジェクトにシェーダソースコード(文字列)をセットする.
 void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
shaderはシェーダオブジェクト(glCreateShaderの返値),countは文字列の数,stringがソースコードを格納した文字列配列,
lengthはstringの長さを示し,NULLを指定すればstringがnull terminatedであることを示す.
+シェーダをコンパイルする.
 void glCompileShader(GLuint shader);

これらの手順を関数にまとめたものを以下に示す.
#code(C){{
/*!
 * GLSLシェーダコンパイル
 * @param[in] target ターゲット(GL_VERTEX_SHADER,GL_FRAGMENT_SHADER)
 * @param[in] shader シェーダコード
 * @return GLSLオブジェクト
 */
inline GLuint CompileGLSLShader(GLenum target, const char* shader)
{
	// GLSLオブジェクト作成
	GLuint object = glCreateShader(target);

	if(!object) return 0;

	glShaderSource(object, 1, &shader, NULL);
	glCompileShader(object);

	// コンパイル状態の確認
	GLint compiled = 0;
	glGetShaderiv(object, GL_COMPILE_STATUS, &compiled);

	if(!compiled){
		char temp[256] = "";
		glGetShaderInfoLog( object, 256, NULL, temp);
		fprintf(stderr, " Compile failed:\n%s\n", temp);

		glDeleteShader(object);
		return 0;
	}

	return object;
}
}}

CompileGLSLShader()にはソースコード文字列を引数として渡さなければならない.
ファイル(*.vs,*.gs,*.fsなど)に記述したシェーダコードを読み込んで文字列化する関数の例を以下に示す.
#code(C){{
/*!
 * GLSLシェーダコンパイル
 * @param[in] target ターゲット(GL_VERTEX_SHADER,GL_FRAGMENT_SHADER)
 * @param[in] fn シェーダファイルパス
 * @return GLSLオブジェクト
 */
inline GLuint CompileGLSLShaderFromFile(GLenum target, const char* fn)
{
	FILE *fp;

	// バイナリとしてファイル読み込み
	fp = fopen(fn, "rb");
	if(fp == NULL) return 0;

	// ファイルの末尾に移動し現在位置(ファイルサイズ)を取得
	fseek(fp, 0, SEEK_END);
	long size = ftell(fp);

	fseek(fp, 0, SEEK_SET);

	// シェーダの内容格納
	char *text = new char[size+1];
	fread(text, size, 1, fp);
	text[size] = '\0';

	fclose(fp);

	// シェーダコンパイル
	printf("Compile %s\n", fn);
	GLuint object = CompileGLSLShader(target, text);

	delete [] text;

	return object;
}
}}

ファイルから読み込むほかに,以下のようにしてソースコード中に直接記述する方法もある.
#code(C){{
#define RXSTR(A) #A

//! Phong 頂点シェーダ
const char phong_vs[] = RXSTR(
// フラグメントシェーダに値を渡すための変数
varying vec3 vPos;
varying vec3 vNrm;

void main(void)
{
	// 頂点位置と法線
	vPos = (gl_ModelViewMatrix*gl_Vertex).xyz;
	vNrm = gl_NormalMatrix*gl_Normal;

	// 描画頂点位置
	gl_Position = ftransform();
}
);
}}


***シェーダのリンク [#b94c2cae]
必要なシェーダ(バーテックス,ジオメトリ,フラグメント)をコンパイル後,それらをリンクして,
1つのGLSLプログラムにまとめる.
+glCreateProgram()でGLSLプログラムオブジェクトを生成.
 GLuint glCreateProgram(void);
ここで生成したプログラムオブジェクトはGLSLでの描画切替にも用いるので,OpenGL描画関数にこの変数を渡せるようにしておくこと.
+glAttachShader()でシェーダオブジェクトをプログラムオブジェクトに関連付ける.
 void glAttachShader(GLuint program, GLuint shader);
programはプログラムオブジェクト(glCreateProgramの返値),shaderは関連付けたいシェーダオブジェクトを指定する.
+glLinkProgram()でプログラムをリンクする.
 void glLinkProgram(GLuint program);
リンクがうまくいったかどうかは,glGetProgramiv()にプログラムオブジェクトとGL_LINK_STATUSを渡すことで調べることができる.

プログラムのリンクを行う関数の例を以下に示す.
#code(C){{

/*!
 * バーテックスとフラグメントシェーダで構成されるGLSLプログラム作成
 * @param[in] vs バーテックスシェーダオブジェクト
 * @param[in] fs フラグメントシェーダオブジェクト
 * @return GLSLプログラムオブジェクト
 */
inline GLuint LinkGLSLProgram(GLuint vs, GLuint fs)
{
	// プログラムオブジェクト作成
	GLuint program = glCreateProgram();

	// シェーダオブジェクトを登録
	glAttachShader(program, vs);
	glAttachShader(program, fs);

	// プログラムのリンク
	glLinkProgram(program);

	// エラー出力
	GLint charsWritten, infoLogLength;
	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);

	char * infoLog = new char[infoLogLength];
	glGetProgramInfoLog(program, infoLogLength, &charsWritten, infoLog);
	printf(infoLog);
	delete [] infoLog;

	// リンカテスト
	GLint linkSucceed = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &linkSucceed);
	if(linkSucceed == GL_FALSE){
		glDeleteProgram(program);
		return 0;
	}

	return program;
}
}}

シェーダソースファイルからコンパイル,リンクを行う関数の例を以下に示す.
返値はrxGLSL型の変数となっており,シェーダファイル名,プログラムオブジェクトなどを格納する構造体である.
#code(C){{
struct rxGLSL
{
	string VertProg;	//!< 頂点プログラムファイル名
	string GeomProg;	//!< ジオメトリプログラムファイル名
	string FragProg;	//!< フラグメントプログラムファイル名
	string Name;		//!< シェーダ名
	GLuint Prog;		//!< シェーダID
};

/*!
 * GLSLのコンパイル・リンク(ファイルより)
 * @param[in] vs 頂点シェーダファイルパス
 * @param[in] fs フラグメントシェーダファイルパス
 * @param[in] name プログラム名
 * @return GLSLオブジェクト
 */
inline rxGLSL CreateGLSLFromFile(const string &vs, const string &fs, const string &name)
{
	rxGLSL glsl;
	glsl.VertProg = vs;
	glsl.FragProg = fs;
	glsl.Name = name;

	GLuint v, f;
	v = CompileGLSLShaderFromFile(GL_VERTEX_SHADER, vs.c_str());
	f = CompileGLSLShaderFromFile(GL_FRAGMENT_SHADER, fs.c_str());

	glsl.Prog = LinkGLSLProgram(v, f);

	return glsl;
}

}}


***シェーダによる描画 [#f7624215]
上記の関数を用いてアプリケーション初期時に以下のようにシェーダプログラムをビルドした後,
 rxGLSL g_GLSL = CreateGLSLFromFile("phong.vs", "phong.fs", "phong");
描画時に[[glUseProgram():http://www.opengl.org/sdk/docs/man/xhtml/glUseProgram.xml]]を用いてGLSL描画に切り替える.
 void glUseProgram(GLuint program);

#code(C){{
	glUseProgram(g_GLSL.Prog);
	glUniform3f(glGetUniformLocation(g_GLSL.Prog, "eyePosition"), eye_pos[0], eye_pos[1], eye_pos[2]);

	glutSolidTeapot(0.5);

	glUseProgram(0);
}}

[[glUniform*():http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml]]はGLSLシェーダのuniform変数に値を渡す命令であり,
その種類は下記参照.


*glUniform [#v798e952]
シェーダ内のuniform変数に値を渡すのに用いるOpenGLの命令.
***変数,定数を用いた受け渡し [#p48f952a]
-浮動小数点数
 void glUniform1f(GLint location, GLfloat v0);
 void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
 void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
 void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
-整数
 void glUniform1i(GLint location, GLint v0);
 void glUniform2i(GLint location, GLint v0, GLint v1);
 void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2);
 void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);

locationにglGetUniformLocationで取得した変数の位置,v0-v3に受け渡す値を指定する.
f接尾辞の場合はfloat,vec2,vec3,vec4とその配列に,
i接尾辞の場合はint,ivec2,ivec3,ivec4とその配列に値を渡すのに用いられる.

***配列を用いた受け渡し [#ffcf416f]
-浮動小数点数
 void glUniform1fv(GLint location, GLsizei count, const GLfloat *value);
 void glUniform2fv(GLint location, GLsizei count, const GLfloat *value);
 void glUniform3fv(GLint location, GLsizei count, const GLfloat *value);
 void glUniform4fv(GLint location, GLsizei count, const GLfloat *value);
-整数
 void glUniform1iv(GLint location, GLsizei count, const GLint *value);
 void glUniform2iv(GLint location, GLsizei count, const GLint *value);
 void glUniform3iv(GLint location, GLsizei count, const GLint *value);
 void glUniform4iv(GLint location, GLsizei count, const GLint *value);

locationにglGetUniformLocationで取得した変数の位置,countは修正する要素の数(GLSL側のuniform変数が配列でないならば1でなければならない),
valueは要素数countの配列のポインタでこの配列の中身が渡される.

***行列の値の受け渡し [#w7fb836e]
 void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 void glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);

上の3つは正方行列用でそれぞれ,2x2,3x3,4x4行列の値(要素数4,9,16)を書き換える.

locationはglGetUniformLocationで取得した変数の位置,countは修正する要素の数(GLSL側のuniform変数が配列でないならば1でなければならない),
transposeにGL_TRUEを指定した場合,行列の要素はrow majorで格納され,GL_FALSEを指定すればcolumn majorで格納される.
valueは要素数countの配列のポインタでこの配列の中身が渡される.


*型 [#dec344e5]
***ベクトル型 [#r7484283]
|型名|説明|
|vec2,vec3,vec4|2D,3D,4D浮動小数点数ベクトル(float)|
|ivec2,ivec3,ivec4|2D,3D,4D整数ベクトル(int)|
|bvec2,bvec3,bvec4|2D,3D,4Dブール値ベクトル(bool)|

***行列型 [#k08913dd]
|型名|説明|
|mat2,mat3,mat4|2x2,3x3,4x4行列(float)|

***テクスチャ型 [#tb04abd6]
|型名|説明|
|sampler1D,sampler2D,sampler3D|1D,2D,3Dテクスチャ(GL_TEXTURE_1D,2D,3Dに対応)|
|samplerCube|キューブマップテクスチャ(TEXTURE_CUBE_MAPに対応)|
|sampler1Dshadow,sampler2Dshadow|1D,2Dデプステクスチャ(GL_TEXTURE_1D,2DでGL_DEPTH_COMPONENTのものに対応)|
|sampler2DRect|2のn乗以外の大きさのテクスチャ(GL_TEXTURE_RECTANGLEに対応,要 [[GL_ARB_texture_rectangle:http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt]])|
|sampler2DRectShadow|2のn乗以外の大きさのデプステクスチャ(要 [[GL_ARB_texture_rectangle:http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt]])|
|sampler1DArray, sampler2DArray|1D,2Dテクスチャ配列(TEXTURE_1D,2D_ARRAYに対応,要 [[GL_EXT_texture_array:http://www.opengl.org/registry/specs/EXT/texture_array.txt]])|
|sampler1DArrayShadow,sampler2DArrayShadow|1D,2Dデプステクスチャ配列(要 [[GL_EXT_texture_array:http://www.opengl.org/registry/specs/EXT/texture_array.txt]])|



*ビルトイン変数 [#h208bef9]
***定数(attribute) [#o090f462]
|変数名|型|説明|
|gl_Vertex|vec4|頂点座標|
|gl_Normal|vec3|頂点法線|
|gl_Color|vec4|頂点色|
|gl_MultiTexCoord*|vec4|テクスチャ*のテクスチャ座標|
|>|>||
|gl_ModelViewMatrix|mat4|モデルビュー行列|
|gl_ProjectionMatrix|mat4|プロジェクション行列|
|gl_ModelViewProjectionMatrix|mat4|モデルビューとプロジェクション行列の積|
|gl_NormalMatrix|mat3|モデルビュー行列の逆行列(法線変換用)|

***変数(uniform) [#wc4b3785]
シェーダー間のデータ受け渡しに使える.
|変数名|型|説明|
|gl_FrontColor|vec4|表面の色|
|gl_BackColor|vec4|背面の色|
|gl_TexCoord[]|vec4|テクスチャ座標|
|>|>||
|gl_Position|vec4|頂点位置(頂点シェーダのみ)|
|gl_FragColor|vec4|フレームバッファに書き込まれる色(フラグメントシェーダのみ)|
|gl_FragDepth|float|フレームバッファに書き込まれるデプス値(フラグメントシェーダのみ)|






*TIPS [#r5fa7025]
**キャスト [#k76bf285]
 warning C7503: OpenGL does not allow C-style casts
Cとはキャストのやり方が違っているので,例えば,
 i = (int)x;
ではなく,
 i = int(x);
とする.

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS