ミップマップ


テクスチャのフィルタ

テクスチャはポリゴンの面に描画されるため、行列変換の影響を受けます
遠くのテクスチャは小さくなりますし、近くのテクスチャは大きく見えます

ある程度のサイズであれば、縮小されても画像を認識することができますが
あまりにも小さくなりすぎると、原型がわからなくなってしまう可能性があります
特に、縮小処理されたテクスチャ画像が特定の移行点で大きく変化してしまうと
連続した画像の場合はあまりに不自然に表示されてしまいます

この問題を解決するために、テクスチャに複数の画像を割り当てることができます
テクスチャはもっとも大きな画像を基準に、解像度の小さい画像を順に格納することができます
これをミップマップと呼び、予め解像度の低い画像を用意しておくことで
テクスチャの解像度が低くなった時、OpenGL が自動的に最適な画像を使うようになるでしょう

ただし、ミップマップに使用する画像は 1 × 1 からもっとも大きい画像サイズの間で
幅、高さ共に 2 の累乗の値にならなければなりません
つまり、64 × 64 をオリジナルのサイズだとすれば、32 × 32、16 × 16、8 × 8 … となります

用意した処理済の小さい画像をテクスチャ・オブジェクトに設定するには
glTexImage2D() 関数から level 引数に適切な値を指定して読み込ませなければなりません
これまでは、オリジナルの画像しか設定しなかったため、この引数には 0 を指定しました
この 0 を基準に 1 ずつ加算しながら画像を設定します
オリジナルが 64 × 64 の画像であれば、32 × 32 の画像が詳細レベル 1 番となります
順に 16 × 16 では 2 番、8 × 8 では 3 番を指定して画像を読み込みます
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

GLubyte tex16[16][16][4];
GLubyte tex8[8][8][4];
GLubyte tex4[4][4][4];
GLubyte tex2[2][2][4];
GLubyte tex1[1][1][4];
GLuint texName;

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

	glBegin(GL_POLYGON);
		glTexCoord2f(0 , 0); glVertex3f(-10 , -20 , -2);
		glTexCoord2f(0 , 20); glVertex3f(-10 , 20 , -2);
		glTexCoord2f(20 , 20); glVertex3f(1000 , 20 , -1000);
		glTexCoord2f(20 , 0); glVertex3f(1000 , -20 , -1000);
	glEnd();
	glFlush();
}

void createTex(int width , int height , int r , int g , int b , void * buf) {
	int i , max = width * height * 4;
	GLubyte * pixels = buf;

	for(i = 0 ; i < max ; i += 4) {
		pixels[i] = r;
		pixels[i + 1] = g;
		pixels[i + 2] = b;
		pixels[i + 3] = 0xFF;
	}
}

int main(int argc , char ** argv) {
	glutInit(&argc , argv);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);

	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);

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

	createTex(16 , 16 , 0xFF , 0 , 0 , tex16);
	glTexImage2D(
		GL_TEXTURE_2D , 0 , GL_RGBA , 16 , 16 ,0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex16
	);
	createTex(8 , 8 , 0x80 , 0x80 , 0 , tex8);
	glTexImage2D(
		GL_TEXTURE_2D , 1 , GL_RGBA , 8 , 8 ,0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex8
	);
	createTex(4 , 4 , 0 , 0xFF , 0 , tex4);
	glTexImage2D(
		GL_TEXTURE_2D , 2 , GL_RGBA , 4 , 4 , 0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex4
	);
	createTex(2 , 2 , 0 , 0x80 , 0x80 , tex2);
	glTexImage2D(
		GL_TEXTURE_2D , 3 , GL_RGBA , 2 , 2 ,0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex2
	);
	createTex(1 , 1 , 0 , 0 , 0xFF , tex1);
	glTexImage2D(
		GL_TEXTURE_2D , 4 , GL_RGBA , 1 , 1 ,0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex1
	);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(160 , 0.75 , 1 , 1000);

	glutMainLoop();

	glDeleteTextures(1 , &texName);
	return 0;
}


このプログラムでは、テクスチャを貼り付けたポリゴンをものすごく小さくして表示しています
前面は -1、最も遠い面は -1000 です。

オリジナルサイズのテクスチャ画像は、わかりやすく赤一色の画像にしています
続いて緑色の画像を用意し、もっとも小さい画像は 1 × 1 ピクセルの青となっています
本来ならば、小さくなることで画像の違和感をなくすためにミップマップを使いますが
この例では、ミップマップの効果を視覚で認識出るように画像の色を変えています

図を見てわかるように、画像が縮小されるとそれに最適なミップマップが選択されています
ミップマップを設定せずにこのプログラムを実行すれば、より効果がわかるでしょう
解像度が低い場所では、テクスチャが正しく表示されなくなることがあります
特に、処理するピクセルに近い位置の色を平均して縮小する場合は致命的な結果になります

テクスチャがどのように画像を縮小するかは、glTexParameter() で設定できます
テクスチャの縮小方法を指定するには、GL_TEXTURE_MIN_FILTER
拡大方法を指定するには GL_TEXTURE_MAG_FILTER を第二引数 pname に指定します

先ほどのプログラムの結果は、ピクセルに近い位置の色を平均して縮小しています
そのため、グラデーションのような効果が現れていますが、好ましくない場合もあるでしょう
テクスチャ・パラメタを設定することでこれを変更することができます
GL_TEXTURE_MIN_FILTER には次の定数を指定することができます

定数解説
GL_NEAREST処理するピクセルの中心に最も近い値を返します
GL_LINEAR処理するピクセルに最も近い 4 つのテクスチャ要素の加重平均を返します
GL_NEAREST_MIPMAP_NEAREST処理するピクセルのサイズに最適なミップマップを選択し
GL_NEAREST を基準として値を返します
GL_LINEAR_MIPMAP_NEAREST処理するピクセルのサイズに最適なミップマップを選択し
GL_LINEARE を基準として値を返します
GL_NEAREST_MIPMAP_LINEAR処理するピクセルのサイズに最適な 2 つのミップマップを選択し
GL_NEAREST を基準としてそれぞれのミップマップから値を生成し
それらの値の加重平均を返します
GL_LINEAR_MIPMAP_LINEAR処理するピクセルのサイズに最適な 2 つのミップマップを選択し
GL_LINEARE を基準としてそれぞれのミップマップから値を生成し
それらの値の加重平均を返します

これらの設定で、画像を縮小する時に用いられる縮小関数が決定します

GL_TEXTURE_MAG_FILTER の場合は、GL_NEAREST と GL_LINEAR を指定できます
テクスチャ画像を拡大する場合は、ミップマップを使わないので他は指定できません
基本的に、加重平均を求めるよりも GL_NEAREST の方が高速に動作します
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

GLubyte tex[4][4][4];
GLuint texName;

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

	glBegin(GL_POLYGON);
		glTexCoord2f(0 , 0); glVertex3f(-10 , -20 , -2);
		glTexCoord2f(0 , 20); glVertex3f(-10 , 20 , -2);
		glTexCoord2f(20 , 20); glVertex3f(1000 , 20 , -1000);
		glTexCoord2f(20 , 0); glVertex3f(1000 , -20 , -1000);
	glEnd();
	glFlush();
}

void createTex(int width , int height , int r , int g , int b , void * buf) {
	int i , max = width * height * 4;
	GLubyte * pixels = buf;

	for(i = 0 ; i < max ; i += 4) {
		pixels[i] = r;
		pixels[i + 1] = g;
		pixels[i + 2] = b;
		pixels[i + 3] = 0xFF;
	}
}

int main(int argc , char ** argv) {
	glutInit(&argc , argv);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);

	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);

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

	createTex(4 , 4 , 0xFF , 0 , 0 , tex);
	glTexImage2D(
		GL_TEXTURE_2D , 0 , GL_RGBA , 4 , 4 , 0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex
	);
	createTex(2 , 2 , 0 , 0xFF , 0 , tex);
	glTexImage2D(
		GL_TEXTURE_2D , 1 , GL_RGBA , 2 , 2 ,0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex
	);
	createTex(1 , 1 , 0 , 0 , 0xFF , tex);
	glTexImage2D(
		GL_TEXTURE_2D , 2 , GL_RGBA , 1 , 1 ,0 ,
		GL_RGBA , GL_UNSIGNED_BYTE , tex
	);
	glTexParameteri(
		GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER ,
		GL_NEAREST_MIPMAP_NEAREST
	);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(160 , 0.75 , 1 , 1000);

	glutMainLoop();

	glDeleteTextures(1 , &texName);
	return 0;
}


このプログラムでは、テクスチャ・パラメタを設定して
加重平均を求めるのではなく、処理ピクセルに近い要素を返す縮小関数を設定しています
先ほどのプログラムと異なり、ミップマップの境目がはっきりしています



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