テクスチャ


画像を貼り付ける

これまでは、単純な画像の表示やポリゴンを表示してきました
もし、これらの技術だけでゲームで使われているようなダンジョンを作ろうとした場合
石造りの壁や複雑な部屋のオブジェクトをポリゴンで表示するため
ものすごく多くの情報量が必要となるうえ、全体が硬いイメージとなり機械的になります

木造の壁や柱を表現するためには、ポリゴンで表現した方形を画像で塗る必要があります
このように、ポリゴンに画像を貼り付けることをテクスチャ・マッピングと呼びます
これは、3次元グラフィックスにおいて最も重要な技術の一つと言えます

テクスチャ・マッピングを行うには、まず、テクスチャを有効にする必要があります
テクスチャには、1次元と2次元の2種類が存在します
1次元のテクスチャは後ほど解説するとして、この場では2次元のテクスチャを作りましょう
2次元テクスチャを有効にするには glEnable() に GL_TEXTURE_2D を指定します

次に、テクスチャを生成しなければなりません
テクスチャを作成するには、テクスチャ・オブジェクトを生成してこれに画像を与えます
テクスチャ・オブジェクトを生成するには glGenTextures()を使います

void glGenTextures(GLsizei n , GLuint * textures);

テクスチャ・オブジェクトを作成するには、まず一意の名前が必要になります
この関数は、textures に配列へのポインタを指定し
n に指定した数だけテクスチャ名を作成してくれます
OpenGL では、テクスチャ名は数値として扱われます

次に glBindTexture() 関数を用いてテクスチャをバインドします
テクスチャのバインドとは、指定した名前のテクスチャを有効にする作業を表します
初めて使われたテクスチャ名が指定された場合は、自動的にオブジェクトが生成されます

void glBindTexture(GLenum target , GLuint texture);

target にはテクスチャの次元を指定します。
ここには GL_TEXTURE_1D または GL_TEXTURE_2D を指定します
texture にはテクスチャ名を指定します

初めて指定された名前であれば、自動的にテクスチャオブジェクトが作られます
そうでなければ、過去に作られたオブジェクトが再び有効になります
このコマンドを使えば、テクスチャ・オブジェクトを簡単に再使用できます
br> テクスチャが有効になれば、あとは画像データを関連付けるだけです
2次元のテクスチャであれば glTexImage2D() 関数を使います
void glTexImage2D(
	GLenum target , GLint level , GLint components ,
	GLsizei width , GLsizei height , GLint border ,
	GLenum format , GLenum type , const GLvoid *pixels  
);
target には、通常 GL_TEXTURE_2D を指定します
場合によっては GL_PROXY_TEXTURE_2D を指定することもあります
後者の使い方は後ほど解説します

level には、0 を基準とした詳細レベル番号を指定します
これも、後ほど解説するので、今は 0 を指定してください

components にはテクスチャ内のカラー要素数を指定します
ここには、カラー要素数に合わせて 1 〜 4 までの値を指定します
OpenGL 1.1 からは、次のいずれかの定数を指定することもできるようになりました

定数
GL_ALPHA, GL_ALPHA4, GL_ALPHA8, GL_ALPHA12, GL_ALPHA16
GL_LUMINANCE, GL_LUMINANCE4, GL_LUMINANCE8, GL_LUMINANCE12
GL_LUMINANCE16
GL_LUMINANCE_ALPHA, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE6_ALPHA2
GL_LUMINANCE8_ALPHA8, GL_LUMINANCE12_ALPHA4, GL_LUMINANCE12_ALPHA12
GL_LUMINANCE16_ALPHA16
GL_INTENSITY, GL_INTENSITY4, GL_INTENSITY8, GL_INTENSITY12, GL_INTENSITY16
GL_R3_G3_B2, GL_RGB, GL_RGB4, GL_RGB5, GL_RGB8, GL_RGB10, GL_RGB12, GL_RGB16
GL_RGBA, GL_RGBA2, GL_RGBA4, GL_RGB5_A1
GL_RGBA8, GL_RGB10_A2, GL_RGBA12, GL_RGBA16

OpenGL リファレンスにこれらの定数の意味が記述されていないので
残念ながら、各定数の具体的な解説をすることができません
数値はビット数を表していますが、OpenGL が必ずその色数を使うとは限りません
この情報から、最も近い内部表現が用いられます
そのため、ビット数を指定しない GL_RGB や GL_LUMINANCE などは柔軟です

width と height には、テクスチャの幅と高さをそれぞれピクセル単位で指定します
ただし、テクスチャの画像サイズは m を正数とした 2m でなければなりません

border にはテクスチャの境界幅を指定します
境界が存在しない場合は 0、存在する場合は 1 を指定します
format にはピクセル・データのフォーマットを、type にはデータ型をそれぞれ指定します
これらは glDrawPixels() の format と type 引数と同じです
そして、pixels に、テクスチャに読み込む画像データを指定します

これで、テクスチャを作成する一連の作業はとりあえず終了です
あとは、頂点を描画するときにテクスチャの座標を設定すれば完成です
テクスチャ・マッピングを有効にした描画の頂点には
オブジェクト座標だけではなく、テクスチャ座標を指定しなければなりません
テクスチャ座標は glTexCoord() を用います
void glTexCoord1d(GLdouble s);
void glTexCoord1f(GLfloat s);
void glTexCoord1i(GLint s);
void glTexCoord1s(GLshort s);
void glTexCoord2d(GLdouble s, GLdouble t);
void glTexCoord2f(GLfloat s, GLfloat t);
void glTexCoord2i(GLint s, GLint t);
void glTexCoord2s(GLshort s, GLshort t);

void glTexCoord3d(
	GLdouble s, 
	GLdouble t, 
	GLdouble r  
);
void glTexCoord3f(
	GLfloat s, 
	GLfloat t, 
	GLfloat r  
);
void glTexCoord3i(
	GLint s, 
	GLint t, 
	GLint r  
);
void glTexCoord3s(
	GLshort s, 
	GLshort t, 
	GLshort r  
);
void glTexCoord4d(
	GLdouble s, GLdouble t, 
	GLdouble r, GLdouble q  
);
void glTexCoord4f(
	GLfloat s, GLfloat t, 
	GLfloat r, GLfloat q  
);
void glTexCoord4i(
	GLint s, GLint t, 
	GLint r, GLint q  
);
void glTexCoord4s(
	GLshort s, GLshort t, 
	GLshort r, GLshort q  
);

void glTexCoord1dv(const GLdouble *v);
void glTexCoord1fv(const GLfloat *v);
void glTexCoord1iv(const GLint *v);
void glTexCoord1sv(const GLshort *v);
void glTexCoord2dv(const GLdouble *v);
void glTexCoord2fv(const GLfloat *v);
void glTexCoord2iv(const GLint *v);
void glTexCoord2sv(const GLshort *v);
void glTexCoord3dv(const GLdouble *v);
void glTexCoord3fv(const GLfloat *v);
void glTexCoord3iv(const GLint *v);
void glTexCoord3sv(const GLshort *v);
void glTexCoord4dv(const GLdouble *v);
void glTexCoord4fv(const GLfloat *v);
void glTexCoord4iv(const GLint *v);
void glTexCoord4sv(const GLshort *v);
新しいテクスチャ座標は (s , t , r , q) に配置されます
s 以外の座標は省略可能で、省略された場合は (s , 0 , 0 , 1) となります
v には、テクスチャ座標を格納する配列へのポインタを指定します
配列には s、t、r、q の順番で座標が格納されていなければなりません

頂点は、設定されているテクスチャ座標を基に画像を描画します
glTexCoord() で指定する座標は、画像に対するの相対的な座標です
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

#define TEXSIZE 64

GLubyte bits[TEXSIZE][TEXSIZE][3];
GLuint texName;

void disp( void ) {
	glClear(GL_COLOR_BUFFER_BIT);
	glBindTexture(GL_TEXTURE_2D , texName);

	glBegin(GL_POLYGON);
		glTexCoord2f(0 , 0); glVertex2f(-0.9 , -0.9);
		glTexCoord2f(0 , 1); glVertex2f(-0.9 , 0.9);
		glTexCoord2f(1 , 1); glVertex2f(0.9 , 0.9);
		glTexCoord2f(1 , 0); glVertex2f(0.9 , -0.9);
	glEnd();
	glFlush();
}

void timer(int value) {
	glRotatef(1 , 0.5 , 1 , 0.25);
	glutPostRedisplay();
	glutTimerFunc(50 , timer , 0);
}

int main(int argc , char ** argv) {
	unsigned int i , j;

	for (i = 0 ; i < TEXSIZE ; i++) {
		int r = (i * 0xFF) / TEXSIZE;
		for (j = 0 ; j < TEXSIZE ; j++) {
			bits[i][j][0] = (GLubyte)r;
			bits[i][j][1] = (GLubyte)(( j * 0xFF ) / TEXSIZE);
			bits[i][j][2] = (GLubyte)~r;
		}
	}

	glutInit(&argc , argv);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
	
	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);
	glutTimerFunc(100 , timer , 0);

	glEnable(GL_TEXTURE_2D);
	glGenTextures(1 , &texName);
	glBindTexture(GL_TEXTURE_2D , texName);

	glTexImage2D(
		GL_TEXTURE_2D , 0 , 3 , TEXSIZE , TEXSIZE ,
		0 , GL_RGB , GL_UNSIGNED_BYTE , bits
	);

	glutMainLoop();
	return 0;
}


このプログラムは、テクスチャを貼り付けた方形が回転します
方形は、単純に画像と 1 対 1 の関係で、プログラム的に生成した画像をそのまま表示しています
テクスチャ・マップを使えば、3次元の世界に萌え萌え美少女を配置することもできちゃいます

テクスチャ・オブジェクトが不要になれば、これを破棄しなければなりません
バインドするテクスチャを他のものに設定しても、リソースはメモリに残ります
これを開放するには glDeleteTextures() 関数を使います

void glDeleteTextures(GLsizei n , const GLuint * textures);

n には削除するテクスチャの数を
texutres はテクスチャ名を格納した配列を指定します
使われていないテクスチャ名が存在する場合は無視されます
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

#define TEXSIZE 64

GLubyte bits[TEXSIZE][TEXSIZE][3];
GLuint texName;

void disp( void ) {
	glClear(GL_COLOR_BUFFER_BIT);
	glBindTexture(GL_TEXTURE_2D , texName);

	glRotated(2 , 1 , 1 , 0.25);

	glBegin(GL_POLYGON);
		glTexCoord2f(0 , 0); glVertex2f(-0.9 , -0.9);
		glTexCoord2f(0 , 1); glVertex2f(-0.9 , 0.9);
		glTexCoord2f(1 , 1); glVertex2f(0.9 , 0.9);
		glTexCoord2f(1 , 0); glVertex2f(0.9 , -0.9);
	glEnd();
	glFlush();
}

void timer(int value) {
	glRotatef(1 , 0.5 , 1 , 0.25);
	glutPostRedisplay();
	glutTimerFunc(50 , timer , 0);
}

int main(int argc , char ** argv) {
	unsigned int i , j;

	for (i = 0 ; i < TEXSIZE ; i++) {
		int r = (i * 0xFF) / TEXSIZE;
		for (j = 0 ; j < TEXSIZE ; j++) {
			bits[i][j][0] = (GLubyte)r;
			bits[i][j][1] = (GLubyte)(( j * 0xFF ) / TEXSIZE);
			bits[i][j][2] = (GLubyte)~r;
		}
	}

	glutInit(&argc , argv);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
	
	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);
	glutTimerFunc(100 , timer , 0);

	glEnable(GL_TEXTURE_2D);
	glGenTextures(1 , &texName);
	glBindTexture(GL_TEXTURE_2D , texName);

	glTexImage2D(
		GL_TEXTURE_2D , 0 , 3 , TEXSIZE , TEXSIZE ,
		0 , GL_RGB , GL_UNSIGNED_BYTE , bits
	);

	glutMainLoop();

	glDeleteTextures(1 , &texName);
	return 0;
}
先ほどのプログラムを改良して、最後にテクスチャの削除を行っています
必ずしも必要な処理ではありませんが、不要なテクスチャは必ず削除しましょう



前のページへ戻る次のページへ