GLSL

OpenGL Shading Language

GLSLプログラミング

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

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

シェーダのコンパイル

GLSLシェーダをOpenGLアプリケーション中でロード,コンパイルする手順は以下.

  1. glCreateShader()でシェーダオブジェクトを生成する.
    GLuint glCreateShader(GLenum shaderType);
    shaderTypeにはGL_VERTEX_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADERのいずれかを指定する.
  2. シェーダオブジェクトにシェーダソースコード(文字列)をセットする.
    void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
    shaderはシェーダオブジェクト(glCreateShaderの返値),countは文字列の数,stringがソースコードを格納した文字列配列, lengthはstringの長さを示し,NULLを指定すればstringがnull terminatedであることを示す.
  3. シェーダをコンパイルする.
    void glCompileShader(GLuint shader);

これらの手順を関数にまとめたものを以下に示す.

/*!
 * 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など)に記述したシェーダコードを読み込んで文字列化する関数の例を以下に示す.

/*!
 * 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;
}

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

#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();
}
);

シェーダのリンク

必要なシェーダ(バーテックス,ジオメトリ,フラグメント)をコンパイル後,それらをリンクして, 1つのGLSLプログラムにまとめる.

  1. glCreateProgram()でGLSLプログラムオブジェクトを生成.
    GLuint glCreateProgram(void);
    ここで生成したプログラムオブジェクトはGLSLでの描画切替にも用いるので,OpenGL描画関数にこの変数を渡せるようにしておくこと.
  2. glAttachShader()でシェーダオブジェクトをプログラムオブジェクトに関連付ける.
    void glAttachShader(GLuint program, GLuint shader);
    programはプログラムオブジェクト(glCreateProgramの返値),shaderは関連付けたいシェーダオブジェクトを指定する.
  3. glLinkProgram()でプログラムをリンクする.
    void glLinkProgram(GLuint program);
    リンクがうまくいったかどうかは,glGetProgramiv()にプログラムオブジェクトとGL_LINK_STATUSを渡すことで調べることができる.

プログラムのリンクを行う関数の例を以下に示す.

/*!
 * バーテックスとフラグメントシェーダで構成される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型の変数となっており,シェーダファイル名,プログラムオブジェクトなどを格納する構造体である.

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;
}

シェーダによる描画

上記の関数を用いてアプリケーション初期時に以下のようにシェーダプログラムをビルドした後,

rxGLSL g_GLSL = CreateGLSLFromFile("phong.vs", "phong.fs", "phong");

描画時にglUseProgram()を用いてGLSL描画に切り替える.

void glUseProgram(GLuint program);
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*()はGLSLシェーダのuniform変数に値を渡す命令であり, その種類は下記参照.

glUniform

シェーダ内のuniform変数に値を渡すのに用いるOpenGLの命令.

変数,定数を用いた受け渡し

  • 浮動小数点数
    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とその配列に値を渡すのに用いられる.

配列を用いた受け渡し

  • 浮動小数点数
    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の配列のポインタでこの配列の中身が渡される.

行列の値の受け渡し

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の配列のポインタでこの配列の中身が渡される.

ベクトル型

型名説明
vec2,vec3,vec42D,3D,4D浮動小数点数ベクトル(float)
ivec2,ivec3,ivec42D,3D,4D整数ベクトル(int)
bvec2,bvec3,bvec42D,3D,4Dブール値ベクトル(bool)

行列型

型名説明
mat2,mat3,mat42x2,3x3,4x4行列(float)

テクスチャ型

型名説明
sampler1D,sampler2D,sampler3D1D,2D,3Dテクスチャ(GL_TEXTURE_1D,2D,3Dに対応)
samplerCubeキューブマップテクスチャ(TEXTURE_CUBE_MAPに対応)
sampler1Dshadow,sampler2Dshadow1D,2Dデプステクスチャ(GL_TEXTURE_1D,2DでGL_DEPTH_COMPONENTのものに対応)
sampler2DRect2のn乗以外の大きさのテクスチャ(GL_TEXTURE_RECTANGLEに対応,要 GL_ARB_texture_rectangle)
sampler2DRectShadow2のn乗以外の大きさのデプステクスチャ(要 GL_ARB_texture_rectangle)
sampler1DArray, sampler2DArray1D,2Dテクスチャ配列(TEXTURE_1D,2D_ARRAYに対応,要 GL_EXT_texture_array)
sampler1DArrayShadow,sampler2DArrayShadow1D,2Dデプステクスチャ配列(要 GL_EXT_texture_array)

ビルトイン変数

定数(attribute)

変数名説明
gl_Vertexvec4頂点座標
gl_Normalvec3頂点法線
gl_Colorvec4頂点色
gl_MultiTexCoord*vec4テクスチャ*のテクスチャ座標
gl_ModelViewMatrixmat4モデルビュー行列
gl_ProjectionMatrixmat4プロジェクション行列
gl_ModelViewProjectionMatrixmat4モデルビューとプロジェクション行列の積
gl_NormalMatrixmat3モデルビュー行列の逆行列(法線変換用)

変数(uniform)

シェーダー間のデータ受け渡しに使える.

変数名説明
gl_FrontColorvec4表面の色
gl_BackColorvec4背面の色
gl_TexCoord[]vec4テクスチャ座標
gl_Positionvec4頂点位置(頂点シェーダのみ)
gl_FragColorvec4フレームバッファに書き込まれる色(フラグメントシェーダのみ)
gl_FragDepthfloatフレームバッファに書き込まれるデプス値(フラグメントシェーダのみ)

TIPS

キャスト

warning C7503: OpenGL does not allow C-style casts

Cとはキャストのやり方が違っているので,例えば,

i = (int)x;

ではなく,

i = int(x);

とする.


トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2024-03-08 (金) 18:06:04