テクスチャマッピングとは,ポリゴンに対して画像(テクスチャ)を貼り付け(マッピング)することです.
元々,textureには「生地,てざわり,質感」といった意味があります.物体の表面的な質感をだすために,表面の画像をポリゴンに貼り付けることによって,よりリアルな描写を目指すものです.
一方,リアルな描写を目的とするだけでなく,ポリゴンオブジェクトの近似に使うこともあります.レンガ造りの壁面を描きたい場合には,単一ポリゴンにレンガの写真を貼りつけることにより,レンガひとつ一つを描画することなしに表示できます(下図).
このテクスチャマッピングはさまざまな可能性を持っています.シーンのリアルさを演出するためだけでなく,映像の歪み補正といった本来の目的とは異なる利用も模索されています.この手の研究は現在も最先端でおこなわれていますが,基本的な手法は以下に集約されます.
ところで,ここまではテクスチャマッピングは極めて表現力の高い技法でいいことずくめのようですが,実は処理がとても複雑のためコンピュータに高度な処理能力を要求します.近年ではテクスチャマッピングをハードウエアで実現し,より高速,高解像度で描画できるようになってきていますが,廉価なハードウエアでは目も当てられないほど遅いものもあり,ハードウエアによって得意不得意が分かれます.テクスチャマッピングを多用する場合には,少なくともテクスチャマッピング機能を持ったビデオカードを使用し,テクスチャメモリの容量を把握しておくことが賢明です.
では,テクスチャマッピングの基本的手法について,以下の節により手順を詳しく見ていきます.
テクスチャとなる画像は,画像はBMPやJPEGなどの外部ファイルを使用するのが通常です.画像には2次元の画素情報が含まれており,それぞれの画素には三原色であるRGB要素が含まれています.画像によってはアルファ値を含んだものもあります.
通常,画像ファイルを使用すると述べましたが,市松模様のようなプログラムで記述できるようなテクスチャはわざわざ画像ファイルを使用するまでもありません.このような幾何学的に定義できるテクスチャは,テクスチャバッファに直接データを書き込む(演習参照)ことで,外部ファイルの読み込みを避けることもできます.
ちなみにOpenGLで使用できるテクスチャバッファの解像度は,縦横ともに2のn乗に準じた数でなければなりません.つまり,128x128や512x256といった解像度でなければ動きません.あらかじめ画像ファイルの解像度を変換しておくか,テクスチャバッファを大き目に作っておいて画像ファイルを配列の一部分に読み込む必要があります.要注意.
OpenGLで外部ファイルを使用する場合は,画像ファイルの読み込み部分を自前で作ってあげる必要があります.したがって,画像フォーマットを熟知しておかなければなりません.知らないフォーマットの画像ファイルはテクスチャとして利用することは困難です.無難なところでは,無圧縮で比較的ファイル読み込みのしやすいTIFF,TGAファイルに変換したテクスチャを使用することをすすめます.
テクスチャバッファとは,フレームバッファやデプスバッファと同じような,画素値を格納する2次元配列です.この配列内の1つ1つの点はピクセルと呼ばず,テクセル(Texel)と呼びます.
テクスチャバッファの格納という主題がついていますが,要するにこの手順では画像ファイルを読み取って,2次元配列にデータを書き込むことをしています.外部ファイルを読み込む場合には,ファイルを読み取って各画素の値をテクスチャバッファにコピーしていきます.テクスチャが幾何学模様の場合には,その幾何学的法則に基づいてテクスチャバッファにデータを書き込んで行きます.
テクスチャバッファは縦横の画素数を持つ2次元配列ですが,それぞれの画素にはRGBのデータ,場合によってはα値が含まれます.アルファ値を含んだテクスチャマッピングでは,そのテクスチャが透明になります.
配列にデータを書き込んだあとは,この配列がテクスチャだよ,という指示が必要です.ここまででテクスチャマッピングの準備が出来ました.
テクスチャマッピングの過程で要となる部分はこの貼り付け処理です.テクスチャの貼り付けには,テクスチャバッファの切り出し点,ポリゴンへの貼り付け点を指定してそれぞれ対応づけます.
下の図はテクスチャバッファを三角形に切り出して,三角形ポリゴンに貼り付けている場合の模式図です.テクスチャバッファ側でどの点を切り出し,ポリゴン側でどの位置に貼り付けるか,という組み合わせは幾通りもあります.思い通りのマッピングをするためには,指定方法をよく理解しておくことが肝要です.
#ref(): File not found: "texturedetach.png" at page "感覚メディア研究室/OpenGL/テクスチャマッピング"
テクスチャバッファ側の切り出し点を指定するために,OpenGLではテクスチャ座標が用いられます.この座標系は横軸をs,縦軸をtとする座標系で張られており(図),テクスチャ座標s,tはそれぞれ[0.0:1.0]の範囲をとります.
配列番号が若いほど左下になります.このため画像は左下から右上に格納されて行きます.上下左右の極性がある画像を使用する場合は注意して下さい.
テクスチャを切り出す場合は,このsとtを指定することで行います.テクスチャ座標の指定には
#highlight(c++:nogutter:nocontrols){{ glTexCoord2f(0.0, 0.0); }} というコマンドを使いますが,引数にs値,t値を指定します.この場合,テクスチャ切り出し点は左下の原点になります.
切り出し点の指定とともにテクスチャ貼り付け点の指定をしなければなりません.これは今まで通りのポリゴン描画と同じで,2次元(3次元)の位置指定をするだけでかまいません.すなわち,
#highlight(c++:nogutter:nocontrols){{
glVertex3f(-5.0,-5.0, 0.0);
}} とすると,直前に呼んだglTexCoord2fに対応した貼り付け点が指定されます.たとえば,
#highlight(c++:nogutter:nocontrols){{
glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-5.0,-5.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-5.0, 5.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f( 5.0, 5.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f( 5.0,-5.0, 0.0); glEnd();
}} と指定すると,下の図のような表示になります.
テクスチャ座標s,tの指定を[0.0:1.0]の範囲を超える場合には,テクスチャが反復されます.
#highlight(c++:nogutter:nocontrols){{
glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-5.0,-5.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-5.0, 5.0, 0.0); glTexCoord2f(2.0, 1.0); glVertex3f( 5.0, 5.0, 0.0); glTexCoord2f(2.0, 0.0); glVertex3f( 5.0,-5.0, 0.0); glEnd();
}} と指定すると,下の図のようにs方向に2回反復表示になります.
また,負の数を使用すると反転表示になります.
サンプルプログラムを用いて、テクスチャマッピングの手順を追っていきましょう。
#highlight(c++:nogutter:nocontrols){{
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#define TEX_HEIGHT 16
#define TEX_WIDTH 16 static GLubyte image[TEX_HEIGHT][TEX_WIDTH][4];
void initTexture(void) {
int i, j, c; for (i=0;i<TEX_HEIGHT;i++) { for (j=0;j<TEX_WIDTH;j++) { c = ( ((i&0x01)==0)^((j&0x01)==0) ); image[i][j][0]= image[i][j][1]= image[i][j][2]=c*255; image[i][j][3]=255; } }
}
void displayTexPolygon(void) {
glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-5.0,-5.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-5.0, 5.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f( 5.0, 5.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f( 5.0,-5.0, 0.0); glEnd(); glDisable(GL_TEXTURE_2D);
}
void display(void) {
static float spin=0.0; glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix(); { glTranslatef(0.0, 0.0,-20.0); glRotatef(spin, 0.0, 1.0, 0.0); glColor3f(1.0, 1.0, 1.0); displayTexPolygon(); } spin+=1.0; glPopMatrix(); glFlush(); glutSwapBuffers();
}
void init(void) {
glClearColor(0.0, 0.0, 0.0, 0.0); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); initTexture(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_WIDTH, TEX_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
}
void reshape(int w, int h) {
glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-5.0, 5.0,-5.0, 5.0, 5.0, 500.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity();
}
int main(int argc, char** argv) {
glutInit(&argc, argv); glutInitDisplayMode (GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGBA); glutInitWindowSize (250, 250); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init(); glutIdleFunc(display); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0;
} }}
void glTexImage2D(GLenum target, GLint level, GLint component, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
外部ファイルを使用したテクスチャマッピングをしてみましょう.
#highlight(c++:nogutter:nocontrols){{
#define HEIGHT 16
#define WIDTH 16 }} の値を適宜変更しましょう.
#highlight(c++:nogutter:nocontrols){{ void initTexture(void) {
FILE *fp; int x, z; /* texture file open */ if((fp=fopen("test.tga", "rb"))==NULL){ fprintf(stderr, "texture file cannot open\n"); return; } fseek(fp, 18, SEEK_SET); for(x=0;x<TEX_HEIGHT;x++){ for(z=0;z<TEX_WIDTH;z++){ image[x][z][2]=fgetc(fp);/* B */ image[x][z][1]=fgetc(fp);/* G */ image[x][z][0]=fgetc(fp);/* R */ image[x][z][3]=fgetc(fp);/* alpha */ } } fclose(fp);} }}
ここで勉強したOpenGLの関数は,
#highlight(c++:nogutter:nocontrols){{ glTexCoord2f() glPixelStorei() glTexParameteri() glTexImage2D() }} です.赤本やGLUTのマニュアルを利用して,復習しておきましょう.
#highlight(end)