キューブマップ(CubeMap)

SAMPLE https://www.dropbox.com/s/rb46ubyouyi6flm/cubemap.exe( キューブマップを利用した環境マップ ) DESC キューブマップとはキューブ(箱)の各6面に2Dテクスチャを割り当て、3つの UV値でテクセルをサンプルする。 UV値を反射ベクトルとみなして、環境マップになどとして利用できる。 VER 1.3 version 1.1 の header にはないため glext.h を利用する GL_REFLECTION_MAP 視点と法線ベクトルから反射ベクトルを計算し その値でキューブマップテクスチャを参照する GL_NORMAL_MAP 法線ベクトルからテクスチャを参照 キューブマップ用のテクスチャは次の条件を満たすこと。
[.] w, h > 0 [.] 6面がすべて同一サイズ && 正方形 [.] 同一ボーダ幅
void App::init() { // 作成するテクスチャオブジェクトはキューブマップ用を指定 glGenTextures( 1, &id ); glBindTexture( GL_TEXTURE_CUBE_MAP, id ); // UVの自動生成方式は ReflectionMap 形式にする glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); // Q の成分は計算する必要はない。 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); // UV自動生成を有効にする glEnable( GL_TEXTURE_GEN_S ); glEnable( GL_TEXTURE_GEN_T ); glEnable( GL_TEXTURE_GEN_R ); // CubeMap 画像 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // 各6面にテクスチャを割り当てる // CubeMap 用の Texture は 縦横サイズが同じでないとだめ int sz = 64*2; void *img = readRawTexture( PATH_TEX"cube.raw", sz, sz*6 ); for( int i=0; i<6; i++ ){ char *p = (char *)img; glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, sz, sz, 0, GL_RGB, GL_UNSIGNED_BYTE, p + (sz*sz*3*i) ); } // WARNING // CubeMap も 2D と同じく補間方法を指定する glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); // ここは CLAMP にしてもかわらない glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_REPEAT ); // POINT // OpenGL に テクスチャのサンプリング方法に CUBE_MAP を使うように指定する // Shader なら 自分で tex2D() などの命令を明示するため不要 // glEnable( GL_TEXTURE_CUBE_MAP ); }
WARNING テクスチャ座標の計算に法線と視線ベクトルの使うため、 法線を定義する。
// 法線を定義して球を描画する。 void drawSphere() { int n = 8*2; int s = 8*2; float r = 25; float dn = 360.0f / n; // 軽度 float ds = 180.0f / s; // 緯度 int i, j; glBegin( GL_QUADS ); for( i=0; i<n; i++ ){ // 横 for( j=0; j<s; j++ ){ // 縦 [ 0 - 180 度まで float x0 = cosf( D2R( i*dn ) ); float z0 = sinf( D2R( i*dn ) ); float x1 = cosf( D2R( (i+1)*dn ) ); float z1 = sinf( D2R( (i+1)*dn ) ); float rs = r*sinf( D2R( j*ds ) ); float y = r*cosf( D2R( j*ds ) ); // Y 軸とのなす角 float ss = sinf( D2R( j*ds ) ); float yy = cosf( D2R( j*ds ) ); glNormal3f( ss*x0, yy, ss*z0 ); glVertex3f( rs*x0, y, rs*z0 ); glNormal3f( ss*x1, yy, ss*z1 ); glVertex3f( rs*x1, y, rs*z1 ); // ひとつ下がる y = r*cosf( D2R( (j+1)*ds ) ); rs = r*sinf( D2R( (j+1)*ds ) ); ss = sinf( D2R( (j+1)*ds ) ); yy = cosf( D2R( (j+1)*ds ) ); glNormal3f( ss*x1, yy, ss*z1 ); glVertex3f( rs*x1, y, rs*z1 ); glNormal3f( ss*x0, yy, ss*z0 ); glVertex3f( rs*x0, y, rs*z0 ); } } glEnd(); }
POINT GL_REFLECTION_MAP の結果が視点座標系と思うので カメラ自体を回す場合は、カメラのワールド行列をかけて反射ベクトルをワールド座標系に戻す。
App::onDraw() { // オブジェクトをカメラから見た位置に置く。 glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); convertCameraSpace( 0, 0, 500 - czoom, cy, cx ); // テクスチャマトリックスを使って、反射ベクトルをワールド座標系に戻す。 glMatrixMode( GL_TEXTURE ); glLoadIdentity(); glRotatef( cx, 0, 1, 0 ); glRotatef( cy, 1, 0, 0 ); // 球を描画する。 drawSphere(); // 元に戻す。 glMatrixMode( GL_TEXTURE ); glLoadIdentity(); }



Texcoordの自動生成

SAMPLE https://www.dropbox.com/s/0que55hvwagjp0i/texgen_world.exe( ワールド座標をテクスチャ座標にする ) テクスチャ座標は明示的に指定する以外に、オブジェクトの座標などから 動的に計算した結果を使うことができる。 環境マップ、投影マップといった動的な処理で使う。 このような場合は glTexcoord() ではなく glTexGen() で自動計算させる。 計算式は次のような 1次結合式をつかうため、計算の元となる x, y, z, w の値とその係数をそれぞれ指定する。
s = a*x + b*y + c*z + d*w u = a*x + b*y + c*z + d*w r = a*x + b*y + c*z + d*w t = a*x + b*y + c*z + d*w
( s, t, r, q ) 毎に tc の自動生成を指定する. POINT 指定するパラメータは2つ 計算式と各係数 GL_EYE_LINEAR GL_OBJECT_LINEAR
// 自動計算で求める Texcoord の成分{ s, t, r, q }を指定する。 glEnable( GL_TEXTURE_GEN_S ); glEnable( GL_TEXTURE_GEN_T ); glEnable( GL_TEXTURE_GEN_R ); 線形結合での各 x, y, z, w の値で利用する座標系を指定する。 // オブジェクト座標系で計算式を利用するには OBJECT // 視線座標系の値を計算式に利用するには EYE_LINEAR // POINT ModelView 変換後の座標を使う 空間全体で TexCoord が共有されるので 投影マッピングなどはこれを利用する // 各係数を指定する // その線形結合の結果が UV 値の値になる // a*x + b*y + c*z + d = S となる
オブジェクトのワールド座標をテクスチャ座標として使うには オブジェクトのワールド座標を計算式を再現すればいいだけ。 オブジェクト座標を使う場合は、 GL_OBJECT_LINEAR を使う。 下の式でいうと x, y, z, w がオブジェクト座標として使われる。 これをワールド座標に変換する行列をかけるだけ。
u = X = ( m00 m01 m02 m03 ) x v = Y = ( m10 m11 m12 m13 ) y Z = ( m20 m21 m22 m23 ) z W = ( m30 m31 m32 m33 ) w u = X = m00*x + m01*y + m02*z + m03*w v = Y = m10*x + m11*y + m12*z + m13*w
void App::onDraw() { // 自動生成を有効化する glEnable( GL_TEXTURE_GEN_S ); glEnable( GL_TEXTURE_GEN_T ); // 計算式の右辺の x,y,z,w をオブジェクト座標に指定する glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); // オブジェクトのワールドの位置 float x = 100; float z = 100; float m[16]; { glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // uv を 1 の範囲におさめるために適当にスケールをかける glScalef( 1.0/1000, 1.0f/1000, 1.0f ); // ワールドの位置に移動した後に glTranslatef( x, z, 0 ); glGetFloatv(GL_MODELVIEW_MATRIX, m); } // テクスチャ座標をワールドに変換する行列の係数をセットする float vs[] = { m[0], m[4], m[8], m[12] }; float vt[] = { m[1], m[5], m[9], m[13] }; glTexGenfv( GL_S, GL_OBJECT_PLANE, vs ); glTexGenfv( GL_T, GL_OBJECT_PLANE, vt ); // 後は通常のオブジェクトを描画する。 glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); convertCameraSpace( 0, 0, 2000 - czoom, cy, cx ); glTranslatef( x, 0, z ); // ハコを描画( UV は明示する必要はない ) drawCube(); }



テクスチャ行列

DESC UV値は利用される前にテクスチャ行列が積算される。 自動生成. 明示的な指定に関係なく適用される。 Default 単位行列 TIP Unit ごとにもつ. 利用しない場合は処理がスキップされる POINT uv animation とかに使用する
// 利用しない場合は以下をコールしておくと安全 glMatrixMode(GL_TEXTURE); glLoadIdentity();