SAMPLE
キューブマップを利用した環境マップ
DESC
キューブマップとはキューブ(箱)の各6面に2Dテクスチャを割り当て、3つの UV値でテクセルをサンプルする。
UV値を反射ベクトルとみなして、環境マップになどとして利用できる。
VER 1.3
version 1.1 の header にはないため glext.h を利用する
GL_REFLECTION_MAP
視点と法線ベクトルから反射ベクトルを計算し
その値でキューブマップテクスチャを参照する
GL_NORMAL_MAP
法線ベクトルからテクスチャを参照
キューブマップ用のテクスチャは次の条件を満たすこと。
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
ワールド座標をテクスチャ座標にする
テクスチャ座標は明示的に指定する以外に、オブジェクトの座標などから
動的に計算した結果を使うことができる。
環境マップ、投影マップといった動的な処理で使う。
このような場合は 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();