\n
[ トップページ ]
[ ___ _CommandPrompt ]
[ ___ _JScript ]
[ ___ _MySQL ]
[ ___ _Cygwin ]
[ ___ _Java ]
[ ___ _Emacs ]
[ ___ _Make ]
[ ___ _Perl ]
[ ___ _Python ]
[ ___ _OpenGL ]
[ ___ _C# ]
[ ___ _StyleSheet ]
[ ___ _C++ ]
[ ___ _Winsock ]
[ ___ _Thread ]
[ ___ _VisualStudio ]
[ ___ _C ]
[ ___ _Win32API ]
[ ___ _Lua ]
[ ___ _PhotoShop ]
ヘッダ検索
■ チュートリアル(Tutorial)
■ ApplicationFW
SAMPLE
シンプルウィンドウ サンプル
DESC
OpenGL Application の フレームワークとして次のような構成を用意しておくと便利。
手間なら GLUT などが利用できる。
// 1 回だけ
virtual void App::onInit();
// 毎 frame ( 描画毎 )
virtual void App::onDraw();
// マウスイベント
virtual void App::onMouseEvent();
アプリケーション毎に具体的な処理をオーバーライドする。
class TestApp : public App {}
int main() {
TestApp app;
// メインループ
app.run();
return 0;
}
REFERENCE 仮想関数でフレームワークをつくる
■ 下準備
POINT
OpenGL は 現在のステート( 色、テクスチャ、変換行列 )を利用してプリミティブを描画する。
ステートを把握しておくことが大切。
変更のない設定は最初にまとめてする。
void init() {
// DepthTest ON
glEnable( GL_DEPTH_TEST );
// Texture なし
glDisable( GL_TEXTURE_2D );
// 画面の背景色を設定する。
glClearColor( 0.5f, 0.5f, 0.5f, 1 );
}
■ 描画毎の設定
描画するタイミングでは、画面をクリア、カメラを設定、図形を描画するという流れになる。
void draw() {
// 画面をクリアする。
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// 現在の カメラ座標系( カメラから見た位置 )の設定をする
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// カメラの画角を設定する
glMatrixMode( GL_PROJECTION );
glFrustum();
// この設定で三角形を描画
glBegin( GL_TRIANGLES );
glVertex3f( 0,0,0 );
glVertex3f( 1,0,0 );
glVertex3f( 1,1,0 );
glEnd();
}
■ 三角形の描画
三角形を描画するには, glBegin() - glEnd() 間で頂点情報を OpenGL へ転送する。
頂点属性は以下のものがあるが、座標( Position )の指定は必須。
Position // 位置
Normal // 法線
Texcoord // テクスチャ座標
Color // 頂点カラー
Fog
Edge
指定した頂点列を組み立てる方法を glBegin() のパラメータとして渡す。
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_POLYGON
GL_POINTS
GL_LINES
GL_LINE_LOOP
GL_LINE_STRIP
GL_QUADS
GL_QUAD_STRIP
■ glVertex
POINT
OpenGL では座標は、行列変換のために同次座標 として扱われる。
// glVertex4f( 1, 0, 0, 1 ) と同じ。
glVertex3f( 1, 0, 0 );
glVertex3f( 1, 1, 0 );
glVertex3f( 0, 1, 0 );
他の頂点属性は glVertex をした時に
Current の State から Copy される。
glBegin();
// Primary Color を赤にする ( w は 1 になる )
glColor3f( 1, 0, 0 );
// Normal , TexCoord も同じように Current が設定される
glNormal3f( 0, 0, 1 );
glTexCoord2f( 0, 0 );
// 以降, Primary Color は赤として 各頂点属性に指定される
glVertex3f( 1, 0, 0 );
glVertex3f( 1, 1, 0 );
glVertex3f( 0, 1, 0 );
glEnd();
■ glNormal
SYNTAX
glNormal( );
DESC
glColor : 同様に glNormal() は定数設定処理が可能
void drawCube3()
{
float s = 100;
static float v[] = {
-s, -s, s,
s, -s, s,
s, s, s,
-s, s, s,
-s, -s, -s,
s, -s, -s,
s, s, -s,
-s, s, -s,
};
static float n[] = {
0, 0, 1, 0, 0, -1,
1, 0, 0, -1, 0, 0,
0, -1, 0, 0, 1, 0,
};
// 6 QUADS
static unsigned short idx[] = {
0, 1, 2, 3, 4, 5, 6, 7,
1, 5, 6, 2, 4, 0, 3, 7,
4, 5, 1, 0, 3, 2, 6, 7,
};
glBegin( GL_QUADS );
for( int i=0; i< 6; i++ ){
// glColor と同じように, 現在の State の影響をうける
glNormal3fv( n + (3*i) );
for( int j=0; j< 4; j++ ){
int p = idx[ 4*i + j ];
glVertex3fv( v + 3*p );
}
}
glEnd();
glDisable( GL_LIGHTING );
}
■ glColor
SYNTAX
glColor
DESC
頂点カラーを設定する。
glVertex() を call すると その時点の Color が設定される。
// 1個のColorで描画
glColor4f( 1,1,1,1 );
glVertex3f();
glVertex3f();
glVertex3f();
// 各頂点で変更
glColor4f( 1,1,1,1 );
glVertex3f();
glColor4f( 1,0,0,1 );
glVertex3f();
glColor4f( 0,0,1,1 );
glVertex3f();
■ REFERENCE(リファレンス)
■ glColorMask
SYNTAX
glColorMask()
DESC
framebuffer の書き込み制御
TRUE : ON
FALSE : 描画 OFF ( 黒が MASK )
■ glRasterPos
SYNTAX
void glRasterPos[234][sidf](
TYPE x,
TYPE y,
TYPE z,
TYPE w
);
DESC
RaterPos( Screen 上に描画する bitmap の位置 ) を指定する
POINT
glRasterPos で指定した位置は VertexTransform の影響をうける
3D 空間での glVertex と同様に考えればいい
RasterPos は次の変換をうける
ObjectSpace ---> ClipSpace ---> WindowSpace
ClipingVolume 内にある場合のみ WindowSpace へ変換される
// glVertex2f() と同じく Texcoord の影響をうける
glRasterPos2f( 1, 1 );
// z = 0, w = 1 としての扱われる
glRasterPos4f( 1, 1, 0, 1 );
POINT
最も初期の FrameBuffer は HostMemory ( Application から直接参照できる Memory )
に格納されていた
■ glDrawPixels
SYNTAX
void glDrawPixels(
unsigned int w, unsigned int h, // Size
int fmt, // format ( RGB )
int type, // 型 ( U8 )
void *data // Pixel Data
);
DESC
CPU memory から FrameBuffer の PixelData に data を転送
( 読み込んだ画像を直接表示するときに利用する )
glRasterPos() で指定した位置に描画される
RasterPos が無効なら描画されない
POINT
glDrawPixels は以下の State の影響をうける
Pixel は 3D の描画と同じく Fragment の変換時に処理をうける
そのため Texture が有効なら 影響をうける
たいていは Default で問題なし
表示されない時は次の State を指定する
for( unsigned int i=0; i< 8; i++ ){
glActiveTexture( GL_TEXTURE0 + i );
glDisable( GL_TEXTURE_1D );
glDisable( GL_TEXTURE_2D );
glDisable( GL_TEXTURE_3D );
glDisable( GL_TEXTURE_CUBE_MAP );
}
// Fragment へ変換する際の FOG Color の着色
glDisable( GL_FOG );
glDisable( GL_DEPTH_TEST );
さらに Window の一部 が 別の Window に隠されている時も問題になる
OpenGL が 隠された 領域から Pixel Data を返せるかどうかは Window System に依存する
POINT
format
Pixel が何を表すか GL に伝える
色なら GL_RGBA
Depth なら GL_DEPTH_COMPONENT
type
Pixel の Data 型
GL の立場で考えれば この情報は必然
data の先頭は void * でもらうため
App::init() {
glClearColor( 1, 0.7f, 1, 1 );
data = new unsigned char[ w*h * sizeof(char) * 4 ];
for( unsigned int i=0; i< w*h; i++ ){
data[4*i + 0] = 255;
data[4*i + 1] = 255;
data[4*i + 2] = 0;
data[4*i + 3] = 255;
}
// 画面 size
float W = 1024.0f;
float H = W/2;
// ModelView 変換はしない
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// 画面 size の大きさになるように Frustum を作成
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, W, 0, H, 1, 1000 );
// 影響のある TEST を OFF にする
// Rasterize 後の FragmentTest で不合格にならないように
glDisable( GL_DEPTH_TEST );
// Fragment 変換時に 不要な色がつかないように
glDisable( GL_TEXTURE_2D );
glDisable( GL_FOG );
glDisable( GL_BLEND );
}
void App::Draw() {
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
static int cnt = 900;
float y = 100;
// x < 0 になると消える
glRasterPos3f( (--cnt), y, -1.5f ); // Z も Frustum にはいるように
// RasterPos が有効かチェックする
{
GLboolean ret;
glGetBooleanv( GL_CURRENT_RASTER_POSITION_VALID, &ret );
psc( "raster pos vaiid %d" , ret );
}
glDrawPixels( w, h, GL_RGBA, GL_UNSIGNED_BYTE, data );
}
TIP
TextureObject を利用した方が処理が早い
EXT:GL_ARB_pixel_buffer_object により高速化可能
如何に処理負荷がかかるかわかる
WARNING
glRasterPos() は VertexTransform の影響をうける
MV = UNIT, Frustum == Window の時に同一の場所になる.
glWindowPos() VER1.4 で直接指定可能.
Rasterize 時の State の影響もうける.
Tex OFF
DepthTest
■ glBitmap
SYNTAX
void glBitmap(
unsigned int w, unsigned int h, // 画像の大きさ
float x, float y, // 開始位置 offset
float xinc, float yinc,// 描画後 の RasterPos の移動量( WindowSpace )
byte *data
);
DESC
大きさ w, h の bimap ( 1pixel 1 bit 画像 )を x, y の位置から
現在の RasterPos に data を描画
描画後 は xinc, yinc だけ RasterPos が移動する
POINT
本来は txt を描画するために利用したが, RasterPos を移動するのに利用する
// "有効な位置" を指定する
glRasterPos( )
// glBitmap() で RasterPos を移動して "有効な位置" からずれても問題なし
glBitmap()
■ glWindowPos
SYNTAX
void glWindowPos[23][sifd]( type x, type x, type z );
VER
1.4
DESC
RasterPos を WindowSpace で指定する
VertexTransform をバイパスする
WARNING
Symbol が見つからないため 中止
■ glClientActiveTexture
SYNTAX
void glClientActiveTexture( GLenum unitTex );
DESC
VertexArray の tc をわりあてる, UnitTex を指定する.
TIP
MultiTex && VertexArray の組あわせで使う
■ glDepthMask
SYNTAX
void glDepthMask( bool flag );
DESC
指定した Buffer の書き込み Mask を設定する.
はいってくる Fragment に対して, 指定した bit と && 操作する.
なので, glDepthMask( true ); -> Depth 値が Buffer に書き込まれる.
TIP
glColorMask( bool red, bool, grn, bool blu, bool alp );
glStencilMask( int mask );
■ glFinish
SYNTAX
void glFinish()
DESC
コマンドが完全に実行されるまで制御を返さない( BlockAPI )
発行済みコマンドを強制実行し、グラフィックス ハードウェアやネットワークが
コマンドの効果が現れた(描画を終了した)ことを保障した時点で制御が戻ります
[GPU]---> おわったよ ----> [CPU]
処理の同期をとる際に利用する
たとえば, 2D 表示を別の PostScript などでする場合.
先に 3次元処理を終了しておく.
WARNING
同期まちをするので, 過度の使用は APP の処理速度をおとす( 特に Network 経由の場合 )
基本的に glFlush() を利用すること。
■ glFlush
SYNTAX
void glFlush()
発行した OpenGL コマンドの実行を強制するという方法が用いられます
Flush == 送り出す.
[ 有限時間内 ]に終了することが保障される
今の FW では CMyGL::flip() で SwapBuffers( hDC ); をしている
App::Draw() の最後.
もし Flush ならば, 処理待ちはしていないことになる.
消すとまったく表示されなくなる.
しかし App::update() は正しい。
SwapBuffers の前に glFinish() を読んでも動作は OK の.
コマンドを発行したからといって、サーバーが描画を終了しているとは限らないのです
ClientServerModel だから, Newwork 経由で毎度命令データを送るわけではない.
でも促したい場合もある
そういう場合は, Flush する( 送り出す )
Graphics 処理は PIPELINE 方式である.
POINT
[CPU] : 描画CMD 発行. : ( 各 CMD の終了をまつ必要なし )
[GPU] : 専用の HW で処理.: CPU が送り出した 頂点を次々に処理する
-> というわけで, CPU は 各 CMD ごとに GPU の処理を待つ必要はない.
さて [CPU] < -> [GPU] が Network ではなれていた場合,
描画 CMD をひとつずつ送るのは、大変 OverHead がかかる.
-> よって, NetworkProgram は CMD を NetworkPacket にまとめる。
しかし [ 1Frame の描画 CMD の発行が終了した ]際は, 強制的に送ってほしい.
Network をはさまない場合は, CMD 発行と同時に即時処理される
次のケースでは glFlush() が有効.
各 Frame の最後で, 残りの CMD をすべて送り出す保障をする.
SystemMemory に描画する SoftwareRenderer で 明示的に描画する際.
■ glClearColor
SYNTAX
void glClearColor( float r, float g, float b, float a );
DESC
glClear に使用する Color を設定する
[0-1] に clamp される
POINT
指定した Alpha 成分も, Clear() するとその値に FrameBuffer の Alpha 成分がなる
Shader, glCopyTexImage2D, Blend 時はこの Alpha 成分を利用することになる
FragmentShader で Fragment.w の値を指定すると Blend State で以下の影響をうける
DST( FrameBuffer) + SRC( Fragment )
WARNING
FrameBuffer に Alpha 成分がなければ Alpha 成分は無視される
TIP
Alpha も設定できることに注目
glCopyTexImage2D() では Alpha 成分も Copy できる
■ glClear
SYNTAX
void glClear( GLbitfield mask )
DESC
Frame Buffer をクリアする
Application は 各 frame の最初に color Buffer と DepthBuffer をクリアする
TIP
一度の 呼び出しで 複数の Buffer をクリアすると高速
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
■ 座標変換(VertexTransform)
DESC
プログラム内での関数名は次のように変換先の空間を指定する.
WARNING
スクリーンスペースでの原点は左下原点となるように指定する。
WARNING
glFrustum, glOrtho をコールすると現在の行列に積算される。
必ず glLoadIdentity() でクリアすること。
glMatrixMode( GL_PROJECTION );
glLoadIdentity(); // 必須
glFrustum();
以下の方法では 現在の PROJECTION 行列に glFrustum() で生成した行列をかけることになる。
glMatrixMode( GL_PROJECTION );
glFrustum();
POINT
頂点を xyz を指定すると w 成分は 暗黙 で 1 になる
POINT
転置する理由
行列は列行関係なく, 最後の 4 つに T がある
( Memory 的には 4*3 がよいので, 転置する )
GL の仕様書で扱う行列は "列順" で表記される
[ 0][ 4][ 8][12]
[ 1][ 5][ 9][13]
[ 2][ 6][10][14]
[ 3][ 7][11][15]
ベクトルは 列ベクトルとして扱う。
[ x ]
[ y ]
[ z ]
[ w ]
これは数式の表記の問題であって, 処理には関係ない。
POINT
守ることは次のことだけ
列順, 行順の混乱しないように, 1 次元配列で考えること。
LoadMatrix() をする際は, 最後の4成分 が移動成分である必要がある。
X 軸 m[0] m[1] m[2] Y 軸 m[4] m[5] m[6] Z 軸 m[8] m[9] m[10] 原点 m[12] m[13] m[14]
例えば、Z 方向に -10 移動する行列は次のように指定する。
float m[] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, -10, 0, // 最後の4要素が移動成分
};
glLoadMatrixf( m );
drawCube();
ProjectionSpace pos.w == ViewSpace pos.z
■ 行列積の順番
POINT
列順, 行順の表記に関係なく
行列変換の API は最後にコールされた順番に頂点が変換される。
T * R * v
glLoadIdentity();
glTranslatef( 1, 0, 0 ); // 横に1移動する
glRotatef( 45, 0, 1, 0 ); // Y 軸に回転してから
drawCube();
R * T * v
glLoadIdentity();
glRotatef( 45, 0, 1, 0 ); // Y 軸中心に 45 度を回転をする。
glTranslatef( 1, 0, 0 ); // 横に1移動してから
drawCube();
カメラ座標系への変換は最後にするので, コードとしては最初にかく。
C * T * R * v
glLoadIdentity();
convertCameraSpace();
// 以下のコードで先ずはワールド空間に配置する。
glTranslatef( 1, 0, 0 );
glRotatef( 45, 0, 1, 0 );
drawCube();
1次式の別表現( 定義 )が行列とベクトルの積
x2 = a*x + b*y
y2 = c*x + d*y
上の式を行列とベクトルの積として定義する
スケール変換を行列積であらわす
V2 = ( sx, 0 ) V
( 0, sy )
■ 行列とベクトルの積
行列とベクトルの積を OpenGL に計算させるには
行列同士の積を利用する。
2個目の行列の第一列をベクトルとみなして、 glMultMatrix() で計算する。
glMatrixMode( GL_MODELVIEW );
glLoadMatrixf( m );
float mv[16] = {
v[0], v[1], v[2], 0,
};
// ベクトルの代わりに
glMultMatrixf( mv );
// 結果をとりだす。
glGetFloatv(GL_MODELVIEW_MATRIX, mv );
// 結果
v[0] = mv[0];
v[1] = mv[1];
v[2] = mv[2];
■ モデルビュー変換
POINT
OpenGL にはカメラをここに置くという命令はない。
モデルのワールドでの位置はカメラが中心の座標系として指定する。
カメラの位置を原点とする右手座標系のため、カメラの正面に置くためには
Z値がマイナスである必要がある。
行列の変換がわかりずらいならば、直接z値にマイナスの値を指定すれば良い。
App::onDraw() {
// 変換をしないように単位行列をロードしておく。
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// 直接マイナスの値を指定する。
float z = -1.0f;
glBegin( GL_TRIANGLES );
glVertex3f( 0, 0, z );
glVertex3f( 0, 1, z );
glVertex3f( 1, 1, z );
glEnd();
}
移動行列を使って、カメラの正面に移動させる。
App::onDraw() {
glMatrixMode( GL_MODELVIEW );
// 同一のモデルを複数の位置で描画する。
glLoadIdentity();
glTranslatef( 0, 0, -1.0f; );
drawTriangle();
glLoadIdentity();
glTranslatef( 1.0f, 0, -1.0f; );
drawTriangle();
}
void drawTriangle() {
glBegin( GL_TRIANGLES );
glVertex3f( 0, 0, 0 );
glVertex3f( 0, 1, 0 );
glVertex3f( 1, 1, 0 );
glEnd();
}
■ 回転とスケール
モデルを回転するには glRotate() を使う。
移動と回転の順番によって結果は異なる。
通常はモデルを回転してから、ワールド( カメラ原点 )の位置へ移動する( 置く )。
[ 単位行列 ] // glLoadIdentity() でカレントの行列は単位行列になる。
[ 移動行列 ] // glTranslatef() で移動行列をかける T * R
[ 回転行列 ] // glRotatef() で回転行列となる。 R
描画コールをした時点でこの行列によって頂点が変換される。
行列操作のコマンドは現在の行列に対して右から掛けられる。
先に回転 glRotate() をコールした後に glTranslate() をコールすると
glRotate 後の座標系で移動することになる。
( または移動してから回転という操作になる。 )
App::onDraw() {
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -1 ); // Z=-1 の位置に置く。
glRotatef( 45, 0, 1, 0 ); // Y軸を中心に45度回転してから
drawTriangle();
}
// DirectX の本では.
---------------------------
射影空間 ( ProjectionSpace )
( z/w ) < -> [0:1] に正規化された NearPlane からの距離.
ProjectionSpace の w は ViewSpace の z と等しい.
■ 投影変換
モデルを配置したら、カメラの位置を焦点として画面にモデルを投影させる。
この変換を投影変換といい、画面に投影することで遠くのものは小さく、
近くのものは大きく見えるようになる。
カメラでいうと画角の調整に相当する。
投影変換をするには glFrustum() を使う。
6つのパラメータで四角錐( 視錐台 )の形を定義する。
この範囲内のものが画面に描画される。
遠くのものほど, 小さく写すので x, y をカメラからの距離に応じて割る。
z の値を変換する1次式
near のときに 0, far の時に 1 となるような係数を求める。
0 = -n * a + b
1 = -f * a + b
// b を消す。
1 = (n - f) * a
1
a = --------
n-f
// b = n*a だから
n
b = --------
n-f
ハードウェアで透視変換をしてもらう。
// 縦横比の調整
X *= W/H;
// 4 次元目に 範囲変換前の Z をセットしておく。これで XY が割られる。
w = z;
// Z の値は 0:1 の範囲に線形変換する
Z =
ハードに渡す頂点は次の条件をみたす必要がある。
自前で投影変換をした後の頂点は以下を満たす必要がある。
■ カメラを動かす
SAMPLE
カメラを動かす
注視点を軸にカメラを回すには、
カメラのワールドの行列の逆行列を各モデルのワールド行列に掛ければよい。
というより、カメラから見た位置に変換するにはカメラの逆行列をかける。
例えば、回転だけで考えると
カメラが右( X=1 の方向 )に90度向けた場合、
カメラを基準に見れば、すべてのモデルを左に−90度回したことと等しい。
移動に関しても同じで
カメラを 右に10移動することと、すべてのモデルを左に−10移動させることは等しい。
以上のことからカメラのワールドの行列がわかれば、
その逆の変換をすればカメラから見た位置が決まる。
カメラのワールドでの位置と回転(向き)から逆の変換をする。
void tranforrmCameraSpace( float tx, float ty, float tz, float rx, float ry ) {
// カメラ位置の逆の変換
glTranslatef( -tx, -ty, -tz );
// カメラの向きの逆の変換
glRotatef( -rx, 1, 0, 0 );
glRotatef( -ry, 0, 1, 0 );
}
各モデルのワールドの位置をカメラから見た位置に変換する。
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, 10 ); // モデルを 0, 0, 10 に置く
tranforrmCameraSpace( 0, 10, 0, 30, 40 ); // 0, 10, 0 に置いたカメラから見た位置に変換する。
drawCube();
glLoadIdentity();
glTranslatef( 1, 1, 1 );
tranforrmCameraSpace( 0, 10, 0, 30, 40 );
drawCube();
■ 行列の結果を取得する
行列の現在の値を取得するには glGet() で取得する。
POINT
glTranslate はそのときの座標系の Local 方向へ移動する
今 移動
[Xx][Yx][Zx][Tx] [Xx][Yx][Zx][Tx]
[Xx][Yx][Zx][Ty] [Xx][Yx][Zx][Ty]
[Xx][Yx][Zx][Tz] [Xx][Yx][Zx][Tz]
[ 0][ 0][ 0][ 1] [ 0][ 0][ 0][ 1]
// 今の座標系のうち, X 方向へ寄与する量 + 元の X 位置
X = Xx * Tx + Yx * Ty + Zx * Tz + Tx( 元の位置 )
結果をチェック
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 1, 2, 3 );
float m[16];
glGetFloatv( GL_MODELVIEW_MATRIX, m );
print( m );
1.000000 0.000000 0.000000 1.000000
0.000000 1.000000 0.000000 2.000000
0.000000 0.000000 1.000000 3.000000
0.000000 0.000000 0.000000 1.000000 // Translation に 1, 2, 3
0.707107 0.707107 0.000000 0.000000
-0.707107 0.707107 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
0.000000 0.000000 0.000000 1.000000
glLoadIdentity();
glRotatef( 45, 0, 0, 1 );
0.707107 0.707107 0.000000 0.000000
-0.707107 0.707107 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
0.707107 0.707107 0.000000 1.000000
// 45 度回転して, そのときの X 軸方向{ 0.707107 0.707107, 0 }へ 1 の長さ移動
glLoadIdentity();
glRotatef( 45, 0, 0, 1 );
glTranslatef( 1, 0, 0 );
0.707107 0.707107 0.000000 0.000000
-0.707107 0.707107 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
1.000000 0.000000 0.000000 1.000000
// 移動した場所で回転
glLoadIdentity();
glTranslatef( 1, 0, 0 );
glRotatef( 45, 0, 0, 1 );
回転と同じく Scale の影響もうける
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glScalef( 0.5, 0.5f, 0.5f );
glTranslatef( 1, 0, 0 );
0.500000 0.000000 0.000000 0.000000
0.000000 0.500000 0.000000 0.000000
0.000000 0.000000 0.500000 0.000000
0.500000 0.000000 0.000000 1.000000
I * MT * MR
■ glMatrixMode
SYNTAX
glMatrixMode
DESC
行列操作をする行列スタックを選択する
Texture Matrix は Texture Unit ごとにある。
TIP
GL_TEXTURE の場合は,
操作するスタックは glActiveTexture() で選択している Unit になる
DEFAULT
単位行列
ERROR
GL_STACK_UNDERFLOW
GL_STACK_OVERFLOW
// UNIT 1 を選択
glActiveTexture( GL_TEXTURE1 );
// UNIT 1 の Texture Matrix を設定
// 以後, Matrix 操作は この Stack が対象
glMatrixMode( GL_TEXTURE );
glLoadIdentity();
glTranslatef( 1, 0, 0 );
// ModelView Stack へ切り替え
glMatrixMode( GL_MODELVIEW );
■ glMultMatrix
SYNTAX
glMultMatrixf( const GLfloat *m )
DESC
スタック最上位の行列に右からかける
カレントの行列を C とすると
glMultMatrix( M ) をコールすると, M を右から掛けることになる。
■ glLoadMatrix
SYNTAX
void glLoadMatrixf( const GLfloat *m);
DESC
指定した行列に置き換える。
m は列優先の行列。
m[0] m[4] m[ 8] m[12] v[0]
m[1] m[5] m[ 9] m[13] v[1]
m[2] m[6] m[10] m[14] v[2]
m[3] m[7] m[11] m[15] v[3]
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 1, 0, 0 );
glTranslatef( 1, 0, 0 ); // この時点で x = 2 の行列
float m[16] = {0};
glLoadMatrixf(m); // これをコールした時点で0行列
■ glRotate
SYNTAX
glRotatef( float angle, float x, float y, float z );
DESC
x, y, z で指定した軸を中心に r 度回転する行列を右からかける。
角度の単位はラジアンではなく、360度での設定。
■ glTranslate
DESC
x, y, z 軸にそった平行移動をする行列を作成して 右からかける
次の行列を glMultMatrix() することと同じ
1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1
次の2つは同じ
glTranslatef( 1, 2, 3 );
float m[] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
1, 2, 3, 1,
}
glMultMatrixf( m );
■ glScale
DESC
x, y, z 軸にそって拡大縮小をする行列を作成して 右からかける
■ 視点座標の定義
DESC
GL の視点座標の定義は以下のもの。
POINT
カメラに写るためにはモデルビュー変換後の Z 座標が - であることが必須になる。
■ glPushMatrix.glPopMatrix
DESC
[ cur Mtx を cp ] したものを最上段に配置
POINT
MtxTex は Unit ごとに設定する. ( 選択は glActiveTexture で )
自前で mtx 計算をする場合は不要な処理のはず.
余計な Error を防ぐために使用を禁止する.
DEFAULT
Stk ごとに 1 つの MtxUnit をふくむ.
EX
GL_MAX_( MODELVIEW | PROJECTION | TEXTURE )_STACK_DEPTH
■ 座標変換 pipeline
次の流れで頂点座標がスクリーン上のピクセル位置まで変換される
[ オブジェクト座標 ]
|
モデリング( ワールド )変換
|
[ ワールド座標 ]
|
視野変換( カメラから見た位置へ変換 )
|
[ 視点座標 ]
|
投影変換 ( 投影面に写像する )
|
[ クリップ空間 ]
|
w 除算
|
[ NormalizedDeviceCoordinate ]
|
ビューポート変換( 印画紙に引き伸ばす )
|
[ スクリーン座標(画面) ]
■ クリップ座標
ModelViewProjection 行列の結果の座標
透視除算( w で割る ) ( 透視投影のみ有効 四角い箱にする )
投影行列が w の値をきめる
平行投影なら w = 1 になる
Clip 座標の点は 以下のようにある
-w < [x,y,z] < w
Frustum の外の Primitive はこの時点で除去
部分的にでる Primitive は, 範囲内だけが Rasterize される
■ 正規化された デバイス座標
-1.0 < [x,y,z] < 1.0
処理
透視除算
w 成分で x, y, z 成分をわる
これによって 遠いものが 小さく見えるように変換される
x : Window の SubPixel
y : Window の SubPixel
■ window 座標
SAMPLE
複数のビューポート
処理
viewport 変換 ( WARNING , x, y だけでなく z も写像される )
変換された z 値は DepthBuffer にはいり、デプステストで使われる。
DESC
左下原点
|
|
|
|
-----------------------------
( [0,0] - [x,y] , Z: depth buf [0:1] ( 正規化された Depth 値 )
0 : DepthBuffer の前面
1 : DepthBuffer の背面
WARNING :
x,y,z は浮動小数点でもつ
ラスタライズのため )
■ glViewport
SYNTAX
void glViewport(
GLInt x, y, // mapping 先の 原点位置 ( 負数も指定できる )
GLInt w, h, // 幅, 高さ
)
DESC
最終的に写像される矩形領域を定義する。
x, y, w, h は Window 左下を原点とするスクリーン座標
DEFAULT
正規化 Device 座標を Window Size 全体に写像する。
Window Size が変更されないなら指定は不要
^--------------------
| |
H|------------ |
| | |
| | |
-------------------->
0,0 W
Normalized Device Coordinate
--------
| |
| |
| |
--------
メタファは印画紙
VertexTransform 後の 値は Film
WARNING
指定した Buffer ( Color | Depth | Stencil )全体をクリアする
一部分ではない
// Viewport 変換もステートのひとつなので
// 好きなタイミングで変更できる
float W = 1024;
float H = 768;
// 左半分 に表示
glViewport( 0, 0, W, H );
drawScene();
// 右半分 に表示
glViewport( 0, W/2, W, H );
drawScene();
■ glFrustum
SYNTAX
glFrustum(
float l, float r, float b, float t,
float n, float f // 視点からクリップ面までの距離 ( 正の値 )
);
DESC
投影変換( 遠くのものを小さく変換する )行列を作成してカレントの行列に掛ける。
// 透視変換行列を指定する。
glMatrixMode( GL_PROJECTION );
WARNING
// 乗算するので 初期化 しとく
glLoadIdentity();
// 通常は左右対称の投影するため,
// left = -right
// bottom = -top の関係が成り立つ。
さらに画面の縦横比(アスペクト)に合わせることで、ウィンドウ変換した際に歪まないようにする。
そのため画面サイズ(アスペクト)と画角から r, l, t, b は決まる。
n, f はニアクリップとファークリップの位置を指定する。
n より手前と f より奥はすべてクリップされて表示されない。
画角と n, f のクリップ面の距離から指定すると直感的にわかりやすい。
WARNING
n, f の値はデプスバッファの精度に影響を与えるためシーンのサイズに合わせる必要がある。
n を小さくして、 f を大きくすれば良いというものではない。
f/n の値を大きくすればするほど、精度が落ちる。
n を 0 に近づけると、 f/n は無限大に発散する。
log2(f/n) bit の精度が失われる。
float l = -0.75;
float r = -l;
float b = -l * h/w;
float t = -b;
glFrustum( l, r, b, t, 1, 1000.0f );
■ glOrtho
SYNTAX
void glOrtho(
GLDouble l, GLDouble r,
GLDouble b, GLDouble t,
GLDouble n, GLDouble f // カメラからの距離
)
DESC
平行な視体積の行列を作成する。
near , far は [ 正負0 ] 可能
視体積 の外の obj はクリップされる
メタファーとしては, カメラから見た世界での BOX を定義
WARNING
行列スタック上で累積するので, glLoadIdentity(); 忘れないこと
ERROR
GL_INVALID_VALUE( l = r, b = t, n = f )
TIP
投影行列が ClipSpace へ変換する. ( Ortho の場合は常に w = 1.0f )
WARNING
ortho proj mtx 内に obj が内包されていない
カメラ座標系( カメラから見た )の点がボックス領域に入る必要がある
glOrtho( 0, 800, 600, 0, 0, 1 );
ERROR
pnt A { -10.0f, 0.0f, 0.0f }
OK
pnt B { 10.0f, 10.0f, 0.0f }
■ gluPerspective
SYNTAX
gluPerspective(
GLDouble fovy, // 縦( Y )方向の角度 ( 単位 degree )
GLDouble aspect, // 縦横比 ( w/h )
GLDouble near, far // 視点からクリップ面までの距離 ( 正の値 )
)
DESC
view frustum を定義する。
アスペクトはビューポートを同じ比率にするのが一般的。
glFrustum などと同じく、カレントの行列に乗算される。
上書きする場合は, glLoadIdentity() を先に呼ぶこと。
fovy/2 とあるので、縦方向半分ではなく, 縦全体の角度指定になる。
/
/
/
-------------------------- 120 度の指定では縦方向はこの範囲が見れる。
\
\
\
この計算と同じになる。
float ang = angle/180.0f * 3.14f;
float t = tan(ang/2) * n;
float b = -t;
float r = t * ratio;
float l = -r;
glFrustum( l, r, b, t, n, f );
■ gluLookAt
SYNTAX
void gluLookAt( GLdouble eyeX, eyeY, eyeZ,
GLdouble centerX, centerY, centerZ,
GLdouble upX,
GLdouble upY,
GLdouble upZ);
DESC
カメラ座標系への変換行列を作成する。( カメラのワールド行列ではない )
この変換により、 注視点は Z軸のマイナスの値に, 視点は原点は移動する。
現在の行列を積算するかどうかは書いてないが, 行列を取得してみればわかる。
UPベクターは 視線ベクトルと平行になってはいけない。
■ DepthBuffer
非線形 ( /w )
指数関数的
次の比率が 目安
16bit Depth Buffer -> far / near = 50
24bit Depth Buffer -> far / near = 10000
■ ProjectionSpace
DESC
ProjectionSpace( 射影空間 ) とその変換について MEMO
DirectX
[-1,-1,0] < -> [1,1,1] の 箱に変換される
W 除算前は ↑の範囲にはない
OpenGL
[-1,-1,-1] < -> [1,1,1] の 箱に変換される
W 除算前は ↑の範囲にはない。
POINT
投影変換 した後の pos.w は ViewSpace における奥行値になる
これは Projection Matrix の 3 行目から明らか
(vpos.x)
(vpos.y)
(vpos.z)
0,0-1,0 (vpos.w) = -vpos.z ( カメラの奥行 )
ということは w 除算とは 奥行で xyz を割ること
, VertexShader で出力する 位置( pos )情報は
w 除算してない
-> どうやら Pipeline の中で行われる
TIP
ShadowMap で利用する Depth 値の算出方法
// [ ModelSpace ] ---> [ ClipSpace ]
float4 pos = mul( mvp, IN .pos );
// POINT
// ここでは まだ w 除算しない
// 理由は 線形補間では 補間式が異なるから
// この違いは shadowmap, priority shadowmap で明らかになる
//
OUT .depth = pos.zzzw;
return OUT ;
// Fragment Shader で w 除算すること
// POINT
// ここで [0:1] の範囲に おさまる
// 逆に w 除算をする前は [0:1] におさまってない
//
float4 col = IN .depth.z / IN .depth/w;
TIP
画面いっぱいに Quad をかく指定
// でも考えてみると Z の指定が これでいい ?
// 変換はしない
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// W = 1 にすること
glVertex3f(-1, -1, -0.3f, 1 );
glVertex3f( 1, -1, -0.3f, 1 );
glVertex3f( 1, 1, -0.3f, 1 );
glVertex3f(-1, 1, -0.3f, 1 );
■ 行列操作
POINT
行列操作 の Command は 行列スタックの最上部につまれ右から乗算される
// Code で記述した順番と反対になる
[ glTranslate ]
[ glRotate ]
{
glRotate(); // 移動した後に回転
glTranslate(); // 先に移動処理がかかる
// 描画
draw();
}
// 数式でかくと
R * T * v
// 言葉で言えば
移動してから, 原点( 0,0,0 )を中心に回転
■ 頂点配列
■ VertexArray(頂点配列)
DESC
描画に使う頂点をひとまとめにして、OpenGL へ転送しておくことで
描画処理を高速化する機能。
頂点を配列として扱うことでまとまった処理ができ、
関数呼び出しの オーバーヘッドを減らすこともできる。
Application は index で頂点を指定する
共有頂点の処理の重複にも効果がある( Cashe )
// 頂点の数だけ関数呼び出しのオーバーヘッドがかかる。
float vtx[3*100][3];
glBegin( GL_TRIANGLES );
for( int i=0; i< 300; i++ ){
glVertex3fv( vtx[i] );
}
glEnd();
// 頂点の配列を指定して一度の描画コマンドで済ます。
glVertexPointer( 3, GL_FLOAT, 0, vtx );
glDrawArrays( GL_TRIANGLES, 0, 300 );
頂点配列を利用する場合は、glBegin() - glEnd() の部分のコードを置き換える。
頂点配列のデータを指定して、頂点配列の使用を有効化した後に描画コールをするだけ。
App::onDraw()
{
// 頂点データ
// 描画コール毎に頂点データが転送されるため、アプリケーション側は描画コールまではデータを保持する必要がある。
float vtx[] = {
0, 0, 0,
100, 0, 0,
50, 200, 0,
};
// 使用する頂点属性を宣言する。( データを指定するだけでは描画に利用されない。)
glEnableClientState( GL_VERTEX_ARRAY );
// 頂点配列として使うデータを指定する。
glVertexPointer( 3, GL_FLOAT, 0, vtx );
// 描画する。
glDrawArrays( GL_TRIANGLES, 0, 3 );
}
SAMPLE
頂点配列
頂点配列には、単一のデータではなく複数の頂点属性を混ぜることができる。
実装によってはこちらの方が高速に処理できる。
struct Vertex {
float pos[3];
float col[3];
};
Vertex v[] = {
{ 0, 0, 0, 1, 0, 0 },
{ 100, 0, 0, 1, 1, 1 },
{ 50, 100, 0, 0, 1, 0 },
};
// インターリーブ型にする場合は stride で間隔を指定する。
glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), v );
glColorPointer ( 3, GL_FLOAT, sizeof(Vertex), v[0].col );
// カラーと座標を使う。
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glDrawArrays( GL_TRIANGLES, 0, 3 );
void drawPrimitiveArray()
{
// Texture の影響をうけないようにする
glDisable( GL_TEXTURE_2D );
const float s = 30;
// Position
static float vtx[] = {
0, 0, 0, s, 0, 0, s, s, 0,
};
// Color
static GLfloat col[] = {
1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
};
// Color , Positon の頂点配列を有効化
// ポインタは場所を指定するだけで使用するにはこの関数をよびだす
//
glEnableClientState( GL_COLOR_ARRAY );
glEnableClientState( GL_VERTEX_ARRAY );
// 頂点配列へののアドレスと要素数を指定する
glColorPointer( 3, GL_FLOAT, 0, col );
glVertexPointer( 3, GL_FLOAT, 0, vtx );
static unsigned short idx[] = { 0, 1, 3,};
// Triangle を描画
glDrawElements( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, idx );
}
}
■ glVertexPointer
SYNTAX
void glVertexPointer(
GLint size, // 頂点座標の要素数 ( XYZ ならば3 )
GLenum type, // データ型
GLsizei stride, // 次の頂点の座標データまでのバイトサイズ。
const GLvoid *pointer // Dataへのポインタ
)
DESC
描画に使う頂点配列のデータ( 場所、型、フォーマット )を指定する。
POINT
stride, pointer パラメータはデータの指定方法によって異なる。
stride
0 : pointer で指定した配列には座標データが連続して並んでいるとみなされる。
N : pointer で指定するのはインターリーブ配列で, 次の座標データまでの間隔をバイトで指定する。
( 実装によってはこちらの方が効率的 )
pointer
VBO がバインドされている場合は VBO のバイトオフセットとして扱われる。
バインドされていない場合は、クライアント側のメモリアドレス。
WARNING
Type にあう Alignment に各要素が配置されている必要がある
■ glTexCoordPointer
DESC
頂点配列のテクスチャ座標データを指定する。
■ glColorPointer
SYNTAX
void glColorPointer(
GLint size,
GLenum type,
GLsizei stride,
const GLvoid *pointer
);
DESC
The glColorPointer function defines an array of colors.
POINT
pointer は VBO がバインドされている場合は VBO 内にあるカラーデータまでのオフセットを指定する。
VBO がバインドされていない場合は アプリケーションのメモリの値になる。
BLEND OFF == glDisable( GL_BLEND );
Fragment col.w = 1; の値が FrameBuffer に格納される.
BLEND ON
Fragment col.w = a; BlendExp にしたがって格納。
要は [ COLOR 成分 ]と同様にふるまう. と考えること。
POINT
Alpha も設定できる。
glCopyTexImage2D() では Alpha 成分も Copy できる. !
■ glVertexAttribPointer
VERSION
2.0
SYNTAX
void glVertexAttribPointer(
GLuint index, // 頂点属性の番号
GLint size, // 要素数( 1−4 )
GLenum type,
GLboolean normalized, // [ -1 : 1 ], [ 0 : 1 ] に正規化するかどうか
GLsizei stride,
const GLvoid * pointer);
DESC
汎用型の頂点データを指定する。
おおまかには glVertexPointer() などと使い方は同じ。
シェーダで追加の頂点属性を指定する時などに利用する。
■ glDrawArrays
SYNTAX
glDrawArrays(
mode, // プリミティブモード
offset, // 頂点配列の開始オフセット
count // 描画に使う頂点数の数
);
DESC
格納した頂点バッファを描画する
glDrawElements とは違い 頂点配列へのインデックスを指定しないので
頂点バッファのデータは描画順にならんでないとだめ
[0]-- [3]
| |
| |
[1]-- [2]
// 四角形をひとつ
// 0 番目から 4 個
glDrawArrays( GL_QUADS, 0, 4 );
■ glDrawElements
SYNTAX
void glDrawElements(
GLenum mode, // Primitive 形式の指定
GLsizei count, // Rendering する頂点の個数
GLenum type, // Index Data の型( GL_UNSIGNED_BYTE | GL_UNSIGNED_SHORT | GL_UNSIGNED_BYTE )
const GLvoid *idx // 頂点配列をさす Index
);
DESC
頂点配列を使った描画コマンドのひとつ
指定した index 列がさす頂点で描画する
glVertexPointer, glNormalPointer, glTexCoordPointer で指定した頂点配列から index を使ってとる
TIP
BufferObject に Bind されているときは
indices は BufferObject への offset として解釈される
// 頂点配列を指定
static float vtx[] = {
0, 0, 0,
1, 0, 0,
1, 1, 0,
};
// 頂点配列へのインデックス配列 0 1 2 を指定する
static unsigned short idx[] = {
0, 1, 2
};
glVertexPointer( 3, GL_FLOAT, 0, vtx );
// BufferObject を Bind しているときは
glBindBuffer( GL_ARRAY_BUFFER, id );
// idx が指す先は BufferObject からの offset として扱われる
glDrawElements( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, idx );
■ glDrawRangeElements
SYNTAX
glDrawRangeElements
DESC
glDrawElements を最適化したもので頂点 index の範囲を制限する
■ glEnableClientState
SYNTAX
void glEnableClientState(
GLenum array
)
DESC
有効にする配列( VertexArray )を指定する
VertexArray の Command を発行するとき, 有効になっている Data のみを利用する
glEnable() と概念は同じだが Client 側の State を変更する
glEnable() は サーバー側のステートを有効にする
glEnableClientState という名前は displaylist に格納できないことに由来している
( data がクライアント側に残るから )
TIP
MultiTexture の場合は. UnitActive Unit のみ影響をうける
// 頂点と法線を使って描画することを宣言
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
// 描画
draw();
■ 頂点バッファオブジェクト(VBO)
SAMPLE
VBO
■ BufferObjectに頂点配列(VertexArray)を配置する
DESC
頂点データをビデオメモリに配置することで描画を高速化する機能のこと。
アプリケーション側のメモリを解放もでき、
また CPU < -> GPU 間での頻繁なデータの転送も不要になる。
DEP
VERSION::1.5
App::onInit() {
glGenBuffers =
(PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers" );
glBindBuffer =
(PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer" );
glBufferData =
(PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData" );
// BufferObject ID を確保
glGenBuffers( n, bufs );
// buffer をバインドして頂点データ, インデックスデータのどちらかを指定する。
// Buffer Object を選択して初期化
glBindBuffer( GL_ARRAY_BUFFER, id );
// 頂点属性
struct Vertex {
float vtx[3];
float col[3]
};
// OpenGL側に記憶領域を確保してもらいアプリケーション側のデータをコピーする
Vertex vtx[] = {
0, 0, 0, 1, 1, 1,
1, 0, 0, 1, 1, 1,
1, 1, 0, 1, 1, 1,
0, 1, 0, 1, 1, 1,
};
glBufferData( GL_ARRAY_BUFFER, sizeof(vtx), vtx, GL_STATIC_DRAW );
// 頂点と同じようにインデックス用のVBOを作成する。
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, id );
// インデックスデータをコピーする。
unsigned short idx[] = { 0, 1, 2, 3 };
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW );
}
App::onDraw() {
// 描画に利用する VBO を指定する。
glBindBuffer( GL_ARRAY_BUFFER, id );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, id );
// VBO を利用するときは, 頂点データのポインタではなく VBO 内のオフセットを指定する。
// インターリーブ型の配列のため stride は頂点ごとのバイト数を指定する。
// pointer 引数には各頂点属性のオフセットを指定する。
glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), 0 )
glColorPointer ( 3, GL_FLOAT, sizeof(Vertex), sizeof( float )*3; )
// 頂点配列を使うときは OpenGL にどこから Data をとるか指定する
// Data を直接送るときも, BufferObject を利用するときも同じ
// 頂点データは既に転送済のため, 要素数、要素型 オフセットのみを指定する。
// VBO の先頭から指定するため offset = 0
//
glVertexPointer( 3, GL_FLOAT, 0, (GLvoid *)( (char*)0 ) );
glVertexPointer( 3, GL_FLOAT, 0, (void *)0 );
// 描画する。
// VBO を利用しているので pointer はメインメモリのアドレスではなく
// インデックス用のVBO内のオフセットを指定する。
glDrawElements( GL_QUADS, 1, GL_UNSIGNED_SHORT, 0 );
}
終了時はバッファオブジェクトを解放する。
App::onQuit() {
glDeleteBuffers( n, bufs );
}
TIP
id = 0; GL は VBO を使用しなくなる
glBindBuffer() ではじめて Bind すると作成
-> 2度目からは, Bind ( glBindTexture とおなじ )
BufferObject , ClientMemory 共に指定する Interface は同一
■ glGenBuffers
SYNTAX
glGenBuffers(
GLsizei n, // 取得する BufferObject の個数
GLuint *buf
)
DESC
n 個の BufferObject の ID を取得する
// Buffer Object の生成
glGenBuffers( 1, (GLuint *)&id_src );
■ glBindBuffer
SYNTAX
glBindBuffer(
GLenum tgt, // BufferObject のタイプ
GLuint id // glGenBuffers で作成した ID
)
DESC
カレントの BufferObject を選択する
新規の Buffer ならば初期化される( 空Data の Object が作成される )
// 選択をはずすには 0 を指定する
glBindBuffer( GL_ARRAY_BUFFER, 0 );
// 頂点配列を格納するためのバッファを作成するには GL_ARRAY_BUFFER
glBindBuffer( GL_ARRAY_BUFFER, id );
// Index を格納するには GL_ELEMENT_ARRAY_BUFFER を指定する
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, id );
■ glBufferData
SYNTAX
glBufferData(
GLenum tgt,
GLsizeiptr size, // バッファオブジェクト全体のサイズ
const GLvoid *data, // コピー元のデータ
GLenum usage
)
DESC
Client Memory から BufferObject へデータをコピーする。
転送先は glBindBuffer() で指定しておく
データは glBufferData() をコールした時点で転送される。
Specifies the expected usage pattern of the data store.
The symbolic constant must be
GL_STREAM_DRAW,
GL_STREAM_READ,
GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW, GL_DYNAMIC_READ, or GL_DYNAMIC_COPY.
usage is a hint to the GL implementation as to how a buffer object's data store will be accessed.
This enables the GL implementation to make more intelligent decisions that may significantly impact buffer object performance.
It does not, however, constrain the actual usage of the data store.
usage can be broken down into two parts:
first, the frequency of access (modification and usage),
and second, the nature of that access.
The frequency of access may be one of these:
STREAM
The data store contents will be modified once and used at most a few times.
STATIC
The data store contents will be modified once and used many times.
DYNAMIC
The data store contents will be modified repeatedly and used many times.
DRAW
The data store contents are modified by the application, and used as the source for GL drawing and image specification commands.
READ
The data store contents are modified by reading data from the GL, and used to return that data when queried by the application.
COPY
The data store contents are modified by reading data from the GL, and used as the source for GL drawing and image specification commands.
// buffer object 用の領域を確保と転送
glBufferData( GL_ARRAY_BUFFER, 3*sizeof(float)*cnt, vtx, GL_STATIC_DRAW );
■ glDeleteBuffers
SYNTAX
glDeleteBuffers(
GLsizei n,
GLuint *bufs
)
DESC
指定 ID の BufferObject を解放する
不要になったら ServerMemory を解放すること
■ テクスチャ(Texture)
SAMPLE
シンプルテクスチャ
ドラックアンドドロップした 2冪サイズのbmp ファイルをテクスチャにする
■ 処理の基本
DESC
テクスチャ処理はフラグメント単位でされる。
UV値は rasterize の補間結果になる
UV の値から画像のテクセル値を取得する
頂点毎の lighting 計算結果後 の fragment に適用される
Fragment に適用される前は, Vertex 毎に tc を割り当てる
ラスタライズ中に, 透視補正補間アルゴリズムを利用して,
フラグメント毎に適用される
TextureObject は高速な VideoMemory に配置される
サイズはなるべく小さい方がいい
map される texel は 左下原点( 0,0 )の直交座標系
t
|
|
|
|-------- s
glTexImage2D() で指定するピクセル列は以下のようになる
(0,0) data の先頭
(1,1) data の最後
POINT
Y 反転するのは
画像 data は左上から始まるため
頂点のカラーを基本色として、テクスチャステージ0から順番に処理される。
ステージ1はステージ0と頂点カラーの混色の結果になる。
頂点カラーは glColor() またはライティングされた色で、
ラスタライズされた時点では各頂点で線形補間された色になる。
混色の方法は glTexEnv() で指定する。
頂点カラー ---> [ ステージ0 ] ---> [ ステージ1 ] ---> ...
はみ出した部分を指定するときは, WRAP_S|T param を利用して mapping を制御する
■ 基本的な流れ
テクスチャオブジェクトはアプリケーションの初期化でする。
unsigned int gId;
void App::onInit()
{
// TextureObject を生成して、そのIDを取得する
glGenTextures( 1, &gId );
glBindTexture( GL_TEXTURE_2D, gId ); // タイプを指定してつくる( GL_TEXTURE_2D )
typedef struct {
unsigned char c[3];
} col;
col O = { 0xFF, 0x00, 0x00 };
col I = { 0xFF, 0xFF, 0xFF };
col img[] = {
I, I, I, I, I, I, I, I,
I, I, I, I, I, I, I, I,
I, O, I, I, I, I, O, I,
I, O, I, I, I, I, O, I,
I, I, I, I, I, I, I, I,
I, O, I, I, I, I, O, I,
I, I, O, O, O, O, I, I,
I, I, I, I, I, I, I, I,
};
// ピクセルデータを指定( ファイルから読んでもよい )
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0,
GL_RGB, GL_UNSIGNED_BYTE, img );
// 貼り方の設定
// 補間方法の設定
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// リピート設定
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 混ぜ方
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
}
void App::draw() {
// 描画時に貼り付けるテクスチャを指定する
glBindTexture( GL_TEXTURE_2D, gId );
// TextureMap を利用することを宣言する
glEnable( GL_TEXTURE_2D );
// UV座標を指定しながら、プリミティブを描画する
float z = -1.0f, s = 200.0f;
glBegin( GL_QUADS );
glTexCoord2f( 0, 0 );
glVertex3f( 0, 0, z );
glTexCoord2f( 1, 0 );
glVertex3f( s, 0, z );
glTexCoord2f( 1, 1 );
glVertex3f( s, s, z );
glTexCoord2f( 0, 1 );
glVertex3f( 0, s, z );
glEnd();
}
不要なテクスチャオブジェクトを破棄するには glDeleteTexture() を使う。
App::onReset() {
glDeleteTextures( 1, &id );
}
// 描画毎に Texture Object を指定する( 変更がないなら そのままで )
App::update() {
// id を指定して TextureObject を指定する.
glBindTexture( id );
// かく
Draw();
}
// 頂点毎に uv 座標を指定して GL に形状 data を送り出す( texture 領域を primitive に関連つける )
UV( 0,0 )は pixel[] の最初を指す
-> BMP Format は 左下から格納されている
TIP
glTexImage2D() になぜ infmt, fmt の指定があるか ?
infmt : 指定 data ptr を { fmt, type }のように読んでね.
そしてそれを, infmt の形でもつ
だから, texture から DEPTH_FORMAT がある
MAX_SIZE = 2048 ( ifmt は考慮していないので注意 )
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &n) // TexUnit 数取得.
Format は MPEG2, AVI をはりつけることも可能. -> image がとれればよい.
■ glBindTexture
SYNTAX
void glBindTexture(
GLenum tgt, // テクスチャオブジェクトのタイプ
GLuint id // バインドするテクスチャオブジェクトのID
);
DESC
TextureObject を選択する
IDをはじめて渡す際に, TextureObject が生成される
この時, TextureObject の param ( == State )は Default で初期化される
ERROR
GL_INVALID_OPERATION:
異なる tgt を指定して bind をする
App::init() {
// 初期化時にテクスチャオブジェクトを生成
glBindTexture();
glBindTexture( GL_TEXTURE_2D, id );
}
App::draw() {
// 描画時に利用するテクスチャを指定する
glBindTexture( GL_TEXTURE_2D, id );
}
■ glDeleteTextures
SYNTAX
void glDeleteTextures( GLsizei, GLint * );
DESC
指定したテクスチャオブジェクトを破棄する。
■ TextureCoordを指定する
SYNTAX
void glTexCoord( TYPE );
DESC
各頂点がテクスチャをサンプルするテクセルの位置をきめる。
TextureCoord { 0, 0 } ならば 画像の左下の色を取得する
// 各頂点を指定する前に、設定する。
// glTexCoord で指定した値は glVertex で影響をうける
//
glBegin( GL_TRIANGLES );
glTexCoord2f( 0, 0 );
glVertex3f( 1, 0, 0 ); // この頂点は UV { 0, 0 } を使う
glTexCoord2f( 0, 0 );
glVertex3f( 1, 0, 0 );
glTexCoord2f( 0, 0 );
glVertex3f( 1, 0, 0 );
glEnd();
WARNING
TextureCoord も頂点と同じく、テクセルをサンプルする前に TextureMatrix が乗算される
自動生成した場合もおなじ
利用しないならば、 TextureMatrix の使用をオフにすることで余計な変換がされることを回避できる。
TextureCoord = M * [ TextureCoord ]
// 頂点と同じように同次座標 であつかわれる
// r = 0, q = 1 となる
glTexCoord2f( 1, 1 );
// q = 1
glTexCoord2f( 1, 1, 1 );
// 明示する
glTexCoord2f( 1, 1, 0, 0 );
■ パラメータ設定
DESC
テクスチャオブジェクトは各種パラメータをもつ
テクスチャをサンプルする時はこの設定の影響をうけるため、
適切な初期設定をしておく。
サンプリング方法の指定
拡大フィルタ
NEARENT : ピクセルに最も近い Texel をサンプル
LINEAR : Pixel を囲む 2 * 2 Texel をサンプル
縮小フィルタ
NEARENT
LINEAR
GL_NEAREST_MIPMAP_NEAREST
: 画面上のテクスチャサイズに合うミップマップから GL_NEAREST でテクセルを決定
GL_LINEAR_MIPMAP_NEAREST
: 画面上のテクスチャサイズに合うミップマップから GL_LINEAR でテクセルを決定
GL_LINEAR_MIPMAP_LINEAR
: 画面上のテクスチャサイズに合う2つミップマップから GL_LINEAR でテクセルを決定
トリリニアサンプリング
GL_NEAREST_MIPMAP_LINEAR
: 2つのミップマップから取得した値の加重平均とをる。
範囲外を指定したときの設定
GL_REPEAT : 繰り返す
GL_CLAMP : 0:1 の範囲に収める
Texel の真ん中をサンプルすると, Texel の色そのものになる
TIP
Texel 格子上の点で Sample すれば均等に 4Texel の平均がとれる
ぼかし効果をつくるなら
[ Texel 幅の整数倍の場所をサンプルすること ]
wrap parameter
App::init() {
// mipmap, 拡縮に対する, parameter
// depth map parameter
// ON
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
WARNING
GL_TEXTURE_MIN_FILTER は default で GL_NEAREST_MIPMAP_LINEAR
( mipmap を利用しない texture では正しく描画されない )
GL_REPEAT は shadow map には不向き
// OFF
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
■ glTexPrameter
SYNTAX
void glTexPrameter(
GLenum tgt,
GLenum name, // 設定するパラメータ名
TYPE prm // 設定する値
)
DESC
TextureObject の 各種パラメータを設定する
// 範囲外のリピート方法を指定する
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 補間方法を指定する
// テクスチャの自動生成にも利用できる
3 種類のカテゴリ.
1.
Rasterize 時の Fragment と Texel の対応関係の指定.
Fragment が Texel より小さい場合: GL_TEXTURE_MAG_FILTER
Fragment が Texel を覆う場合: GL_TEXTURE_MIN_FILTER
WARNING :
Default が Mipmap を利用する
GL に自動生成させる( 画像を指定する前に )
VER:1.4
{
glTexPrameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
glTexImage2D();
}
tc の wrapping Param : はみだしたときどうするか ?
texture image を指定する際は mipmap をすべて指定する必要あり
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); で自動生成 && 自動再生成
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
において, Default が MipMap のため, 必ず変更すること.
■ 画像を割り当てる
■ glTexImage2D
SYNTAX
void glTexImage2D(
GLenum tgt, // Texture Object の TYPE ( GL_TEXTURE_2D | CUBE_X )
GLint level, // ミップマップのレベル ( 利用しないならば 0 )
GLint internalFormat, // OpenGL 内部でどの成分(種類)にデータをもつか( RGBA, Depth, Luminance, Intensity )
GLsizei w, h, // 画像サイズ
GLint border,
GLenum format, // 設定する画像データのフォーマット
GLenum type, // 各色要素のデータ型
void *texel // 画像データへのポインタ
)
DESC
バインドした TextureObject に画像データを設定する。
画像データは OpenGL に転送されるので
アプリケーション側はメモリを保持しなくてもよい。
設定する画像データは, テクスチャの左下から右上に向かって割り当てられる。
-----------------End
Start-------------
format, type, のパラメータで, アプリケーションから転送する画像データの構成を OpenGL に知らせて
internalFormat に従って OpenGL 内でもつ画像データ構造を決める。
// GL_RGB を internalFormat で指定すると, alpha 成分は 1 として扱われる
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, data );
// GL_RGBA を internalFormat で指定すると, alpha 成分は画像データの alpha が利用される。
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
GL_RGBA は 各要素を 32 bit でもつ.
説明としては, どの成分( 明るさ, Depth, RGBA )が Texel として利用するかを決める.
TIP
GL_RGBA : 4個の要素をもち、カラー画像として使われる。
GL_LUMINANCE : 1個の要素をもち、グレースケールとして使われる。
GL_LUMINANCE_ALPHA : 2個の要素をもち、グレースケールとして使われる。
GL_DEPTH_COMPONENT : 1 成分 -> ( shadow map はこれ )
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
GL_RGB, GL_UNSIGNED_BYTE, image );
// GL_DEPTH_COMPONENT は特定の解像度を要求しない
// 要求に対して近い Format が選択される
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, w, h, 0,
GL_RGB, GL_UNSIGNED_BYTE, image );
ミップマップ( あらかじめ縮小した画像のこと )の指定をする場合は、
レベルを変更しながらサイズが半分にした画像データを指定する。
for( i=0; i< 3; i++ ){
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, i,
GL_RGBA, GL_UNSIGNED_BYTE, image[i] );
}
WARNING
各列は default で 4byte アラインメントになっている必要がある。
( GL がそう解釈するから )
■ glTexImage3D
SYNTAX
void glTexImage3D();
NOTE
3D Texture は Tool では作成できない
そのため Program で動的に作成する
POINT
DirectX の場合 [0:1] で決めるが
GL は [0:255] で決める
3 次元 texture は短い video clip が可能
unsigned int i, j, k;
for( i=0; i< 32; i++ ){
for( j=0; j< 32; j++ ){
for( k=0; k< 32; k++ ){
texvol[i][j][k][0] = (unsigned char)( 255 * myRand() );
texvol[i][j][k][1] = (unsigned char)( 255 * myRand() );
texvol[i][j][k][2] = (unsigned char)( 255 * myRand() );
texvol[i][j][k][3] = 255;
}
}
}
glGenTextures( 1, &idVol );
glBindTexture( GL_TEXTURE_3D, idVol );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
MyTex->load3dData( "noise3d.bmp" , 32, 32, 32, texvol );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
■ glTexSubImage2D
SYNTAX
void glTexSubImage2D(
GLenum tgt, // Texture Object の TYPE ( GL_TEXTURE_2D | CUBE_X )
GLint level, // ミップマップレベル ( 利用しないならば 0 )
GLint x, y, // オフセット
GLsizei w, h,
GLint border,
GLenum format, // 設定する画像のフォーマット
GLenum type, // 各要素 の Data 型
// ( GL_BYTE | GL_UNSIGNED_BYTE | GL_SHORT | GL_UNSIGNED_SHORT )
// ( GL_INT | GL_FLOAT | GL_UNSIGNED_INT )
// ( GL_BITMAP )
void *texel
)
DESC
バインドした既存のテクスチャの一部( SubSet )を 別の画像で置き換える。
glTexImage2D の 部分集合版なので、パラメータもほぼ同じ。
ERROR
GL_INVALID_ENUM
target が GL_TEXTURE_2D でない場合
format が容認された定数でない
type が型定数でない場合
GL_INVALID_VALUE
width < 0 | height < 0
GL_INVALID_OPERATION
type がGL_UNSIGNED_SHORT_5_6_5 で、format が GL_RGB でない場合
■ glTexSubImage1D
SYNTAX
void glTexImage2D(
GLenum tgt, // Texture Object の TYPE ( GL_TEXTURE_2D | CUBE_X )
GLint level, // Image の MipMap Level ( 利用しないならば 0 )
GLint x, y, // Offset
GLsizei w, h,
GLint border,
GLenum format, // 設定元 Image の Format
GLenum type, // 各要素 の Data 型
// ( GL_BYTE | GL_UNSIGNED_BYTE | GL_SHORT | GL_UNSIGNED_SHORT )
// ( GL_INT | GL_FLOAT | GL_UNSIGNED_INT )
// ( GL_BITMAP )
void *texel
)
DESC
現在の Texture の一部を 別の Texel でおきかえる
float texel[128];
glTexImage1D( GL_TEXTURE_2D, 0, texel );
■ テクスチャのブレンド方法を指定する
SYNTAX
void glTexEnv(if)( GLenum tgt, GLenum namePrm, TYPE prm )
DESC
テクセルの色をフラグメントの色とのブレンド方法を指定する。
この設定は テクスチャユニットごとに設定される。
加算処理とかできる
ライティングした色と乗算する。(デフォルト)
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// 線形補間するには GL_COMBINE
// tex0 * ( 1 - a ) + tex1 * a
//
glActiveTexture( GL_TEXTURE0 );
glActiveTexture( GL_TEXTURE1 );
glTexEnvi( GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvi( GL_TEXTURE_ENV_MODE, GL_COMBINE );
GL_BLEND を指定したときは画像のアルファ値は使われないで
かわりに画像の RGB 値が
別に設定した色と下地の色との混合比に使われる
この色は
glTexEnvf() の pname に GL_TEXTURE_ENV_COLOR を指定する
この出力画像では
テクスチャの暗い部分に下地の色(color: 赤)が現れ
明るい部分に別に定義した色 (blend: 緑) になる
---
Rv, Gv, Bv, Av → テクスチャ環境関数の結果
Rt, Gt, Bt, At → テクスチャ色
Rf, Gf, Bf, Af → フラグメント色
Rc, Gc, Bc, Ac → テクスチャ環境色 (GL_TEXTURE_ENV_COLORを参照)
---------------------------
// Multi Texture なら一番↓ ( TEXTURE0 ) から順に評価
[ ] < --- Texture
[ ] < --- Texture
[ ] < --- Texture
[ ] < --- Fragment
// 計算をするときは 次の 2 つが対象になる
[ ] < ---- 新しく貼ろうとしている Texture ( 基本形式 はこの Texture が対象 )
[ ] < ---- 既に計算すみの Fragment
計算方法は
貼る対象の Texture の 内部フォーマットによって決まる
Format は複数あるが 6 つの基本形式に分類される
そして計算時は RGBA 形式に変換されて扱われる
GL_ALPHA 0, 0, 0, A
GL_LUMINANCE 0, 0, 0, 1
GL_RGB R, G, B, 1
GL_RGBA R, G, B, A
GL_RBGA : 基本 Texture 形式
■ GL_MODULATE
各成分をかける( Alpha も乗算される )。
ライティング結果と乗算される デフォルトの設定。
out = src * texcol;
Rv = Rf * Rt
Gv = Gf * Gt
Bv = Bf * Bt
Av = Af * At
■ GL_DECAL
ステッカーを貼った効果を出す時に使う。
テクスチャのアルファ成分でマスクをする。
透明にしたい場所のアルファ値を 0 にしておく。
0ならば src の色となる。
out = texcol * texcol.a + ( 1 - texcol.a ) * src
Rv = Rf * (1-At) + Rt * At
Gv = Gf * (1-At) + Gt * At
Bv = Bf * (1-At) + Bt * At
Av = Af
■ GL_REPLACE
テクスチャの色で置き換える。
WARNING
利用すると, ライティング結果も無効になる。
out = texcol;
Rv = Rt
Gv = Gt
Bv = Bt
Av = At
■ GL_ADD
■ GL_COMBINE
演算子なども指定して細かく混色の方法を指定する。
次のような計算式を指定する。
次の要素を指定する。
// 算術演算子を指定する。
// 1番目の引数を指定する。
// 2番目の引数を指定する。
GL_RGB
TIP
高速化のためには, Texture の内部 Format のサイズを小さくする
32bit -> 16bit におとす
これをするには _EXT とつく 拡張フォーマットを使う
// この式の [ 意.味 ] がわからん
Rv = Rf * (1-Rt) + Rc * Rt
Av = Af * At
■ 画像ファイルから読み込む
SAMPLE
png ファイルを読み込んでテクスチャにする
REFERENCE ファイルダイアログ
REFERENCE nmake
■ raw
.raw 形式のファイルを読む
raw 形式とはヘッダ情報がないデータだけのファイル
void *readRawTexture( const char *path, int w, int h, int nrElement )
{
FILE *fp = fopen( path, "rb" );
// 画像サイズ
int sz = w * h * nrElement;
// バッファ確保
char *buf = new char [sz];
// ヘッダ(余計な情報)がないため fread() のみで読める
fread( buf, sz, 1, fp );
fclose( fp );
return buf;
}
■ ppm
raw 形式と同様に ppm というシンプルなフォーマットも利用できる。
フォーマットは以下のとおり
ファイル形式識別記号
横ピクセル数 縦ピクセル数
最大輝度
画像データ(RGB順,左から右,上から下へ走査した順
POINT
ヘッダ部分の改行コードは \n である必要がある。
そのため、必ずバイナリモードでオープンすること。
P6
1024 1024
255
..........
ppm 形式として 24bit RGB で保存する。
保存した画像は gimp などで開くことができる。
FILE *fp = fopen( "test.ppm" , "wb" );
int w = 256, h = 256;
int nrElement = 3;
// 画像サイズ
int sz = w * h * nrElement;
// ヘッダ
fprintf( fp, "P6\n%d %d\n255\n" , w, h );
// バッファ確保
char *buf = new char [sz];
// 画像データ
memset( buf, 0, sz );
fwrite( buf, sz, 1, fp );
fclose( fp );
■ bmp
ウィンドウズの標準画像フォーマットなので、ペイントでもつくれる。
フォーマットは先頭にヘッダがあり、その後にピクセルデータ列がある。
-------------------
ヘッダ
INFOヘッダ
-------------------
ピクセルデータ
-------------------
ピクセルデータ列は 青、緑, 赤 の順番。
左下から右上の順番にピクセルデータが格納されている。
( ファイルの末尾は右上のピクセルデータ )
バイナリのテキストエディタで開くとわかる。
OpenGL のテクスチャの順番と同じため、 glTexImage2D() にそのまま渡すことができる。
WARNING
png などは順番が異なるため 上下反転などの処理が必要になる。
[B][G][R][B][G][R] ...
--------------> エンド
| |
| |
| |
| |
| |
| |
スタート ----------->
アルファがある時( 32bit )は BGRA の順番になる。
[B][G][R][A][B][G][R][A] ...
WARNING
横のバイト数は 4バイトアラインメントされている必要がある。
4 で割り切れない時は, padding を考慮して読み書きする必要がある。
次の場合は 4 の倍数になるので padding は考えなくてもいい。
■ png
画像サイズを小さくしたい場合は png 形式を使う。
可逆圧縮なので、画像も劣化しない。
次の順番で画素データがつまっている。
先頭が左上で 最後が右下
-------------------
START --------->
/
/
/
/
/
/
---------> END
-------------------
ライブラリ libpng と zlib に依存するためネットから取得する。
wget http://jaist.dl.sourceforge.net/project/libpng/libpng15/1.5.10/lpng1510.zip
wget http://jaist.dl.sourceforge.net/project/libpng/zlib/1.2.7/zlib127.zip
解凍する。
unzip lpng1510.zip
unzip zlib127.zip
ビルドする時の makefile の記述にあわせてディレクトリ名を変更する。
mv lpng1510 png
mv zlib-1.2.7 zlib
たとえば次のディレクトリの状態になる
d:/png
/scripts
d:/zlib
/win32
VisualStudio 2008 のコマンドプロンプトを起動して namke を使ってビルドすれば完了。
cd zlib
nmake -f win32/Makefile.msc
cd png
nmake -f scripts/makefile.vcwin32
とりえあえず RGB 8bit を読む簡単なサンプル
#include "png/png.h"
#include "zlib/zlib.h"
#include "zlib/zconf.h"
#pragma comment(lib,"png/libpng.lib" )
#pragma comment(lib,"zlib/zlib.lib" )
// PNG構造体の取得
png_structp pPtr = NULL;
pPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
// PNG情報構造体の取得
png_infop info_ptr = NULL;
info_ptr = png_create_info_struct(pPtr);
png_bytep pData = 0; //PNGからピクセルデータを受け取るバッファ
png_bytepp row_pointers = 0; //PNGから受け取った画像行の先頭ポインタ
// fread を利用したデフォルトの読み方にまかせる。
png_init_io( pPtr, fp );
// 情報ヘッダの読み込み
png_read_info( pPtr, info_ptr );
png_uint_32 W, H; // 横, 縦ピクセル数
int bit_depth; //ビット深度
int color_type; //PNG画像の形式
png_get_IHDR(pPtr,info_ptr,&W,&H,&bit_depth,&color_type,NULL,NULL,NULL);
//すべての設定を登録
png_read_update_info( pPtr, info_ptr );
//イメージを格納するメモリーを割り当てる為の情報を取得
png_size_t rowbytes = png_get_rowbytes(pPtr,info_ptr);//行データのバイト数
int channels = (int)png_get_channels(pPtr, info_ptr); //チャンネル数
// 画像データをうけとるバッファ
pData = new BYTE[ rowbytes * H ];
row_pointers = new png_bytep[H];
// 行配列にフラット領域のそれぞれの位置を設定します
for (UINT i=0;i< H;i++) {
row_pointers[i] = pData + i*rowbytes;
}
// イメージを読む
png_read_image( pPtr, row_pointers );
// 後始末
delete[]row_pointers;
png_read_end(pPtr,NULL);
printf( "img W %d H %d channels %d rowbytes %d\n" , W, H, channels, rowbytes );
// raw データとして出力して確認
{
FILE *fp = fopen( "d:/test_png_out.raw" , "wb" );
fwrite( pData, H*rowbytes, 1, fp );
fclose(fp);
}
読み込み方法をカスタマイズするには
png_set_read_fn() にコールバックとして渡す。
例えば、アーカイブ, リソースなどのメモリ上に読み込み済みの png ファイルから
png イメージを取得するには次のようにする。
struct ReadContext
{
// 読み込み元のデータの場所
const char *ptr;
// 現在の位置
int i;
ReadContext( const char *p, int _i)
: ptr((char *)p), i(_i)
{}
};
png_init_io() の代わりにコールバックを指定する。
void readPng( char *pSrc ) {
#if 0
png_init_io( pPtr, fp );
#else
ReadContext ctx( pSrc, 0 );
png_set_read_fn( pPtr, &ctx, pngReadProc );
#endif
}
コールバックされるので好きなように実装する。
ライブラリが指定する data という場所に length byte を読み込んであげる。
void pngReadProc( png_structp png, png_bytep data, png_size_t length )
{
// png 構造体から png_set_read_fn() で渡したポインタを取得する。
ReadContext *p = (ReadContext *)png_get_io_ptr(png);
// png file を読みこみ済みなので memcpy をする。
memcpy
// 書き込み先はシステムからもらう
data,
// 読み込み元はこちらの指定
p->ptr + p->i,
length
);
p->i += length;
}
先にpngファイルを読んでから、読み込み処理をする。
FILE *fp = fopen("d:/test.png" ,"rb" );
char *pSrc = new char[ filesz ];
fread( pSrc, filesz, 1, fp );
fclose( fp );
readPng( pSrc );
■ nconvert
nconvert を利用すると、任意の形式に変換できる。
nconvert -out bmp test.ppm
nconvert -out png test.ppm
nconvert -out jpg test.ppm
nconvert -out tga test.jpg
raw 形式には変換できないらしい。
nconvert -out raw test.ppm
一覧をみるには help をつかう。
windows icon も変換できる。
REFERENCE リソーススクリプトのかきかた
nconvert -out ico test.ico
上書きをする
nconvert -overwrite -out ico test.ico
出力ファイル名を指定する
nconvert -out png -o d:/out.bmp d:/src.cmp
サイズを変更する
nconvert -out bmp -resize 64px 64px d:/src.bmp
比率を保持する
nconvert -out png -ratio -resize 1000px 0px d:/test.bmp
ログをOFFにする
nconvert -quiet -out png d:/src.bmp
■ キューブマップ(CubeMap)
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();
■ マルチテクスチャ(MultiTexture)
SAMPLE
マルチテクスチャ
■ 全体の流れ
DEP
glext.h
VER
1.3
DESC
ひとつの物( Primitive )に絵( Texture )をたくさん貼り付ける
[0][1][2][3][4][5][6][7]
テクスチャユニット( サンプラー )は8つあり、テクスチャオブジェクトを設定することで
一度の描画で複数のテクスチャを参照できる。
ペイントソフトのレイヤーと同じ概念。
POINT
TextureObject を 各 Unit に bind する
OpenGL は GL_TEXTURE0 から順にフラグメントに適用する
テクスチャマトリックスは ユニットごとに持つ
POINT
テクスチャユニットの設定に関連したAPIは以下のもの。
現在のユニットの影響を受けるため、把握しておくこと。
ユニットをサンプラーとして考えると必要な設定が理解しやすい。
DEFAULT
GL_TEXTURE0
#include < GL/glext.h>
void App::init() {
PFNGLACTIVETEXTUREPROC glActiveTexture =
(PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture" );
// 操作するテクスチャユニットを指定する
// 一枚のみを利用しているときはこれが暗黙の指定になっている。
glActiveTexture( GL_TEXTURE0 );
// UNIT0 にテクスチャオブジェクトをセットする
glBindTexture( GL_TEXTURE_2D, id );
// ブレンド方法の指定
// 次の設定が最も simple blend 方法 ( 各 unit にすること )
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// 2D Texure をサンプリング処理を有効化する。
glEnable( GL_TEXTURE_2D );
// 2 番目のユニットを指定
glActiveTexture( GL_TEXTURE1 );
// 上と同じように設定するだけ
glBindTexture( GL_TEXTURE_2D, id2 );
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
// 描画時は各UNITごとに Texture座標を指定する
App::draw() {
}
glClientActiveTexture(); で texture 座標を割り当てる texture unit を選択
glTexCoordPointer(); で texture 座標配列を ( 明示的 | buffer object ) から指定
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
WARNING
描画毎に Unit は使い回すので state の管理は必要
glTexEnv(); の計算は texture object の intenral format にも依存
&& alpha 成分も計算の対象
( glTexImage(), TexParamter(), TexEnv(), BindTexture() )
はカレントに対して有効
// 単一の tex unit に戻す
glActiveTexture( GL_TEXTURE1 );
glDisable( GL_TEXTURE_2D );
glActiveTexture( GL_TEXTURE0 );
MultiTexture 用に複数の TextureCoord を指定する方法は3つある。
次のように使い分ける.
MultiTex && VertexArray
-> glTexCoordPointer();
MultiTex && Scratch
-> glMultiTexCoord();
MultiTex && 自動生成.
-> glTexGen(); // ( Shader 利用するなら不要 )
/ / Multi tc の指定方法.
// Server 側の Unit ( State ) を選択する
glActiveTexture();
// 複数の tc を VertexArray に指定する.
// VertexArray の tc を割り当てる [ U.n.i.t ] を指定する.
glClientActiveTexture();
// Client 側の UnitTex を指定する.
glEnableClientState( );
// TexCoord を指定
glTexCoordPointer( 2, GL_FLOAT, 0, buf );
複数の TexCoord を指定する
glClientActiveTexture( GL_TEXTUREn );
glTexCoordPointer():
glClientActiveTexture() が指す TexUnit に TexCoord を割り当てる.
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
も指定している, TexUnit が利用される.
WARNING
glBegin() - glEnd() では指定できない
POINT
Texcoord.w の成分により w 除算される
glTexCoord2f( 1, 0 ); == glTexCoord4f( 1, 0, 0, 1 );
glTexGen をした時も, Texture Matrix の影響をうける
WARNING
部分的に glTexGen はできない ?
GL_TEXTURE_2D && glTexGen は併用できない ? -> それはないはず
void drawCube()
{
float s = 30;
// 8 頂点
static float v[] = {
-s, -s, s, s, -s, s, s, s, s, -s, s, s,
-s, -s, -s, s, -s, -s, s, s, -s, -s, s, -s,
};
// 6 QUADS の index
static unsigned short idx[] = {
0, 1, 2, 3, 4, 5, 6, 7,
1, 5, 6, 2, 4, 0, 3, 7,
4, 5, 1, 0, 3, 2, 6, 7,
};
// 各面の Normal
static float n[] = {
0, 0, 1, 0, 0, -1,
1, 0, 0, -1, 0, 0,
0, -1, 0, 0, 1, 0,
};
// 各面の Texcoord
static float t[] = {
0, 0, 1, 0, 1, 1, 0, 1,
};
glBegin( GL_QUADS );
for( int i=0; i< 6; i++ ){
// 法線を設定
// ここの影響をうける
glNormal3fv( n + (3*i) );
// world となるように normal を指定する ?
// 先ほどの見え方から推測すると,
// ここで指定した normal が , 視線空間上で mapping される
//glNormal3fv( n );
for( int j=0; j< 4; j++ ){
// 現在の idx
int p = idx[ 4*i + j ];
glMultiTexCoord2f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1] );
// z も指定してみる
//glMultiTexCoord3f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], t[2*j + 1] );
// w も指定してみる
//glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], t[2*j + 1], 1 );
// 前言撤回 -> ここも影響をうける
glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], -1, -1 );
glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], -1, -100 );
// w 除算される -> 頂点処理 と同じ流れ
//glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], -1, 0.5f );
glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], -1, 2 );
//glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], -1, 1 );
// Z 値はなんでもいい
glMultiTexCoord4f( GL_TEXTURE1, t[2*j + 0], t[2*j + 1], 10000, 1 );
glVertex3fv( v + 3*p );
}
}
glEnd();
}
■ glMultiTexCoord
SYNTAX
void glMultiTexCoord( GLenum unitTex, TYPE tc );
DESC
指定したテクスチャユニットに Texcoord をセットする
POINT
glTexCoord(); は実は. glMultiTexCoord( GL_TEXTURE0, ); と同じ.
// ユニット1 に UV を指定する
float t = { 0, 0, 1 };
glMultiTexCoord3fv( GL_TEXTURE0, t );
float t = { 0, 0, 1 };
glMultiTexCoord3fv( GL_TEXTURE1, t );
glBegin( GL_QUADS );
for( int i=0; i< 4; i++ ){
glVertex3fv( vtx );
}
glEnd();
拡張機能のため 関数ポインタを取得しておく
PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv;
PFNGLMULTITEXCOORD3FVPROC glMultiTexCoord3fv;
glMultiTexCoord2dv =
(PFNGLMULTITEXCOORD2DVPROC)wglGetProcAddress("glMultiTexCoord2dv" );
glMultiTexCoord3fv =
(PFNGLMULTITEXCOORD3FVPROC)wglGetProcAddress("glMultiTexCoord3fv" );
より複雑に指定するには GL_COMBINE mode を使う
( Shader を使った方が簡単だけど )
{
// Preset の補間( GL_ADD, GL_MODULATE )ではなく, CUSTOM の補間を使う
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
// 線形補間 を指定
// ここも複数指定できる
// { GL_COMBINE_RGB }
// { GL_COMBINE_A }
//
// GL_REPLACE a0
// GL_MODULATE a0a1
// GL_ADD a0 + a1
// GL_ADD_SIGNED a0 + a1 - 0.5f
// GL_SUBSTRACT a0 - a1
// GL_INTERPOLATE a1 * ( 1 - a2 ) + a0 * a2;
//
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
///glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD_SIGNED );
// 補間する 係数 を指定する ( Alpha 成分のみが使われる )
static const float c[] = { 1, 1, 1, 0.5f };
//glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, c );
// 次のような計算をすることになる
// GL_INTERPOLATE
// V1 * ( 1 - V2 ) + V0 * V2;
// POINT
// V2 も Vector 扱い
// rgb, a を別途指定できる
// f.r = ( 1 - a2.r ) + a0.r * a2.r;
// f.g = ( 1 - a2.g ) + a0.g * a2.g;
// f.b = ( 1 - a2.b ) + a0.b * a2.b;
// f.a = ( 1 - a2.a ) + a0.a * a2.a;
// Document からの仮定( この順番 )
//
// a0 : 今 処理中の UNIT がサンプルする Texture 色
// a1 : 前の Texture UNIT の結果
//
// 今 Alpha にしているのは UNIT1 の結果の RGB の値
// CubeMap が明るい -> a0 が優先
// 変数 a2 に何を使うか 決める
// GL_PREVIOUS == 前の UNIT の結果
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS );
//glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE );
//glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
// a2.rgb にどの値を使うか決める
//
// a2.r = TexUnit1.r
// a2.g = TexUnit1.g
// a2.b = TexUnit1.b
//
// WARNING
// GL_SRC_RGB という 定数 はなし
// a2.rgb に 前の Unit の結果のどの成分を使うか ?
// GL_SRC_COLOR なので
// a2.rgb = UnitPrev.rgb
//
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR );
//glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_RGB );
//glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_ALPHA );
//glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT );
// Primary Color( 頂点色 )を指定すれば Texture の色は無視される
//
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
}
■ glActiveTexture
SYNTAX
PFNGLACTIVETEXTUREPROC glActiveTexture;
#if defined(WIN32)
glActiveTexture =
(PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture" );
#endif
■ ProxyTexure
DESC
proxy texture は GL に texture のサポートを問い合わるもの
圧縮 texture も対応している
■ 投影マップ(ProjectionMap)
SAMPLE
投影マップ
画面に投影されるようなテクスチャ座標を計算するには
通常のオブジェクトの変換と同じ工程を使う。
モデルビュー、プロジェクション、w除算された頂点座標は
画面に対して [ -1 1 ] の範囲に収まる。
これをさらに [ 0 1 ] の範囲にずらせば画面に投影されるテクスチャ座標が求まる。
POINT
2次元テクスチャ座標は u, v の2つを指定するが、
投影マップの場合はw除算の処理が必要なため、w成分( GL_Q )も求めて置く必要がある。
Z成分( GL_T )の値は求める必要はない。
#define W 1024
#define H 512
App::onDraw() {
// UVの変換行列を合成する
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.5f, 0.5f, 0.5f);
glScalef(0.5f, 0.5f, 0.5f);
gluPerspective(60.0, (float)W/H, 1.0, 2000.0);
gluLookAt( n, n, n, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// 合成された変換行列を取得する。
float m[16];
glGetFloatv(GL_MODELVIEW_MATRIX, m );
// UVの自動生成を有効化する。
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_GEN_T );
glEnable( GL_TEXTURE_GEN_R ); // おまけ
glEnable( GL_TEXTURE_GEN_Q );
// 自動生成の計算式にオブジェクト空間の頂点座標を使う。
glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
// 線形結合する式の係数を並べておく。
float vs[] = { m[0], m[4], m[8], m[12] };
float vt[] = { m[1], m[5], m[9], m[13] };
float vr[] = { m[2], m[6], m[10], m[14] };
float vq[] = { m[3], m[7], m[11], m[15] };
// 合成した変換行列をオブジェクトの頂点に掛ければ画面を覆うようにUVが計算される。
glTexGenfv( GL_S, GL_OBJECT_PLANE, vs );
glTexGenfv( GL_T, GL_OBJECT_PLANE, vt );
glTexGenfv( GL_R, GL_OBJECT_PLANE, vr );
glTexGenfv( GL_Q, GL_OBJECT_PLANE, vq );
// 投影したいテクスチャをバインドする
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, id );
// モデルビュープロジェクション変換を設定する。
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluPerspective(60.0, (float)W/H, 1.0, 2000.0);
gluLookAt( n, n, n, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// 平面を描画する。
// UVは自動で計算されるので座標は明示( glTexCoord() )しない。
drawPlane();
}
■ シャドウマップ(ShadowMap)
SAMPLE
シャドウマップ
DESC
影を落とすはライトから投影したデプステクスチャを利用する。
そのため影を落としたい範囲にフラスタムがはいるようにする。
#define W 1024
#define H 1024
void App::onInit()
{
// シャドウマップ用のテクスチャオブジェクトを生成
glGenTextures( 1, &idShd );
glBindTexture( GL_TEXTURE_2D, idShd );
// シャドウマップ用のテクスチャオブジェクトを生成。
// 後でデプスバッファの値を格納するため ピクセルデータは設定しないでおく
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, W, H, 0,
GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
// 拡大縮小、リピート設定
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// フラグメントの tc.R( Z )成分と シャドウマップテクスチャの値を比較する
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
// 比較関数の指定( より小さい == より暗い == より手前 )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
// 比較の結果を輝度値として適用する
glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE );
}
ライトの位置からシャドウマップを投影するため、
投影マップと同じようにテクスチャ座標を動的に計算する。
今度はカメラではなくライトの位置から投影するのでライトの場所にカメラを置く。
void App::onDraw()
{
float n = 500;
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.5f, 0.5f, 0.5f);
glScalef(0.5f, 0.5f, 0.5f);
gluPerspective(60.0, (float)W/H, 1.0, 2000.0);
gluLookAt( n, n, n, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // ライトの位置
// 合成された変換行列を取得する。
float m[16];
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] };
float vr[] = { m[2], m[6], m[10], m[14] };
float vq[] = { m[3], m[7], m[11], m[15] };
// 合成した変換行列をオブジェクトの頂点に掛ければ画面を覆うようにUVが自動計算される。
glTexGenfv( GL_S, GL_OBJECT_PLANE, vs );
glTexGenfv( GL_T, GL_OBJECT_PLANE, vt );
glTexGenfv( GL_R, GL_OBJECT_PLANE, vr );
glTexGenfv( GL_Q, GL_OBJECT_PLANE, vq );
}
// UVの自動生成を有効化する。
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_GEN_T );
glEnable( GL_TEXTURE_GEN_R );
glEnable( GL_TEXTURE_GEN_Q );
// 自動生成の計算式にオブジェクト空間の頂点座標を使う。
glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
}
// デプステクスチャを作成するためライトの位置にカメラを置いてレンダリング。
// 前面を描いてしまうと、奥行きの判定の時に光のあたる面にも影が落ちてしまう。
// そこで裏面のみを描画する。
glEnable( GL_CULL_FACE );
glCullFace( GL_FRONT );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// ライトの位置にカメラを置く。
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective(60.0, (float)W/H, 1.0, 2000.0);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( n, n, n, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
drawCube();
// レンダリングされたデプスバッファの値をデプステクスチャへセットする。
glBindTexture( GL_TEXTURE_2D, idShd );
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, W, H );
// 描画面を前面に戻して、2パス目の描画をする。
glEnable( GL_CULL_FACE );
glCullFace( GL_BACK );
// 画面をクリアする。
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// 適当な位置にカメラを置いて物体を描画する。
{
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( -n, n, -n, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// 影を落とすために、シャドウマップをバインドする。
// テクスチャ座標は自動計算される設定になっている。
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, idShd );
drawCube();
drawPlane();
}
}
■ アニメーション(Animation)
SAMPLE
アニメーション
■ Debug ( Tex に関連する )
TextureObject の State と UnitTex の State を混合していませんか ?
glTexParameter(); が文字通り. TextureObject の Param を設定する
MAG|MIN_FILTER
WRAP_(S|T|R)
GENERATE_MIPMAP
Tex が完全( 次の要件を満たすか Check )
Tex が不完全だと, ActiveUnit が無効であるような振る舞いをする.
-> Tex なしのような効果になる.
要件
w, h > 0
if ( GL_TEXTURE_MIN_FILTER ) Mipmap
Mipmap 前提の MIN_FILTER なのに, Mipmap なしの画像を渡していないか ?
よくある間違い.
TextureObject と同一タイプを tgt に指定すること
glEnable( GL_TEXTURE_2D|CUBE_MAP );
異なる TYPE を OFF にする.
-> 実は優先度を指定したりする.
■ ブレンディング(Blending)
SAMPLE
加算ブレンド
ブレンドを利用した残像エフェクト
DESC
ブレンディングを指定することで、フレームバッファとフラグメントの色を混色できる。
半透明のオブジェクトを描画したり、特殊なエフェクトを追加できる。
POINT
半透明を描画するときは, Blend ステートを設定すること。
ブレンド処理はフラグメントの色が決定した後にフレームバッファにある色とされる。
頂点処理 ---> フラグメント処理 ---> ブレンド処理 ---> ピクセルの色が決定
ブレンド方法は1次式の内容を指定するだけ。
合成色 = Fragment * sFactor + FrameBuffer * dFactor
NOTE
係数は [0:1] の範囲であることが前提
合成結果 は [0:1] にクランプされる
アルファ成分も計算される
POINT
ブレンド係数の指定は複数あるが、意味のある組み合わせは以下のような内容。
// アルファブレンド
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 加算
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// 上書き ( デフォルト )
glBlendFunc(GL_ONE, GL_ZERO);
// 光学フィルター
// 色成分 の割合を調整する
//
glBlendFunc( GL_ZERO, GL_SRC_COLOR );
■ ブレンド処理による残像
前のフレームの画像と現在のフレームの描画結果を混色することで残像のような効果をだせる。
前のフレームの結果をテクスチャに保持しておき、現在のフレームとアルファブレンドする。
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// シーンを描画する。この時点でフレームバッファは現在のフレームの結果になる。
setProjection( 60/1, 1.0f*W/(float)H, 1, 1000 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
convertCameraSpace();
drawCube();
// テクスチャとして、とっておいた前のフレームとブレンドする。
// 前のフレームをバインド
glBindTexture( GL_TEXTURE_2D, id );
// ブレンド指定
glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 画面いっぱいに矩形を描くための変換
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glDisable( GL_DEPTH_TEST );
// 頂点カラーのアルファ成分を使って、フラグメントのアルファを指定する。
glColor4f( 1, 1, 1, 0.99f )
{
glBegin( GL_QUADS );
float s = 1;
float z = -1;
glTexCoord2f( 0, 0 ); glVertex3f( -s, -s, z );
glTexCoord2f( 1, 0 ); glVertex3f( s, -s, z );
glTexCoord2f( 1, 1 ); glVertex3f( s, s, z );
glTexCoord2f( 0, 1 ); glVertex3f( -s, s, z );
glEnd();
}
// 合成結果を次のフレームに使うため、とっておく。
glBindTexture( GL_TEXTURE_2D, id );
glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, W, H, 0 );
■ glBlendFunc
SYNTAX
void glBlendFunc(
GLenum s, // フラグメントの重み
GLenum d // フレームバッファの重み
);
DESC
合成するときのブレンド率を指定する。
DEFAULT
GL_ONE
GL_ZERO
■ glBlendEquation
SYNTAX
void glBlendEquation( unsigned int )
DESC
Fragment, FrameBuffer の合成方法( 計算式 )を指定する
DEFAULT
GL_FUNC_ADD
// 加算
// S + D
glBlendEquation( GL_FUNC_ADD );
// 減算
// S - D
glBlendEquation( GL_FUNC_SUBSTRACT );
// 減算
// D - S
glBlendEquation( GL_FUNC_REVERSE_SUBSTRACT );
// min, max
// min( S, D )
// max( S, D )
glBlendEquation( GL_MIN );
glBlendEquation( GL_MAX );
POINT
Alpha 成分の指定もすることになる
GL_ZERO
RGB A
( 0, 0, 0 ) 0
GL_ONE
RGB A
( 1, 1, 1 ) 1
GL_SRC_ALPHA
RGB A
( As, As, As ) As
GL_SRC_COLOR
RGB A
( Rs, Gs, Bs ) As
sfactor specifies which of nine methods is used to scale the source color components.
dfactor specifies which of eight methods is used to scale the destination color components.
The eleven possible methods are described in the following table.
Each method defines four scale factors, one each for red, green, blue, and alpha.
In the table and in subsequent equations, source and destination color components are referred to as ( Rs, Gs, Bs, As )
and ( Rd, Gd, Bd, Ad ). They are understood to have integer values between 0 and ( kR, kG, kB, kA ), where
kc = 2mc - 1
TIP
半透明の面を正しく描画するにはアプリケーションは次の順番で描画をする。
1. 不透明な面をかく
2. 半透明を 視点から遠い順にかく
// 不透明 Object を描画
drawOpaqueObject();
// depth かきこみ OFF
glDepthMask( GL_FALSE );
// ブレンド機能を有効にする。
glEnable( GL_BLEND );
// ブレンド方法を指定する。
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Fragment の Alpha を透明にする
// 画面奥から順番に描画
//
// Alpha を 1 以下に
// Texture のアルファ値も乗算されるはず
float a = 0.5f;
float s = 100.0f;
glColor4f( 1, 1, 1, a );
glBegin( GL_QUADS );
glVertex4f( 0, 0, z, 1 );
glVertex4f( s, 0, z, 1 );
glVertex4f( s, s, z, 1 );
glVertex4f( 0, s, z, 1 );
glEnd();
SRC = fragment col, DST = buf col
// blend 処理 ON
glEnable( GL_BLEND );
// blend の計算式を選ぶ
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// 加算する場合
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
■ アルファテスト
DESC
指定した条件に合格したフラグメントのみがフレームバッファへ更新される。
適用する、しないの2値の処理のため、ブレンド処理の方が柔軟に制御できる。
glEnable( GL_ALPHA_TEST );
// > 0.1 なら合格
glAlphaFunc( GL_GREATER, 0.1f );
■ カリング(Culling)
DESC
Primitive の裏側を破棄する機能
デフォルトではポリゴンは両面が描画される。
これによって Rasterize 処理が不要になるため処理が軽減される
閉じた Object は通常この設定を ON にする。
表は 頂点 が半時計まわり
// Culling 機能 ON
// Default : OFF
glEnable( GL_CULL_FACE );
// Culling する面の向きを指定
glCullFace( GL_FRONT );
// 面を描画して確認
drawQuad();
■ 描画モード
描画方法を変更するには glPolygonMode() を使う。
ラスタライズ方法が変更されて、塗り、ライン、点に変更できる。
glShadeModel(GLenum mode)
■ glPolygonMode
SYNTAX
glPolygonMode(GLenum face, GLenum mode)
// 前面をラインで背面をポリゴンで描画する。
glPolygonMode(GL_FRONT, GL_LINE);
■ glShadeModel
SYNTAX
glShadeModel( GLenum face, GLenum mode )
glShadeModel( GL_SMOOTH );
■ Debug(デバッグ)
POINT
簡単なものから順番にして, 正しく Rendeing されているか確認すること
追加して問題がおきれば, そこが原因
DESC
OpenGL のエラーを取得するには glGetError() を利用する。
POINT
OpenGL はエラーを起こすような命令がくると、その処理を実行しないでエラーフラグを立てる。
アプリケーションは継続するためアプリケーション側はチェックをする必要がある。
WARNING
コンパイルエラーにはならないが、OpenGL への命令としてはエラーになるケースがあるため
エラーチェックをする必要がある。
無効なマクロ定数の指定はコンパイルエラーにならないが、
OpenGL はエラーを発生させて命令を実行しない。
// これは Compile ERROR ではないが、OpenGL への命令としてはエラー
glTexEnvi( GL_S, GL_TEXTURE_ENV_MODE, GL_OBJECT_PLANE );
POINT
常に Error Check する習慣をつけること
ただし Cost は 0 ではないので , Release 時は消す。
Error の仕様は変更されるため, Error 内容に依存した Code はかかない
Error が発生する場合, GL は Command を無視する
マクロを利用して, リリース時はオフになる仕組みを用意しておく。
#ifdef _DEBUG
# define CHECK_ERROR checkError()
#else
# define CHECK_ERROR
#endif
void checkError() {
GLenum err = glGetError();
if ( err != GL_NO_ERROR ) {
printf( "OpenGL ERROR code %d: %s\n" , err, (const char *)gluErrorString( err ) );
}
}
App::init() 時に一度. App::onDraw() の最後でするのがコツ。
正しく描画されない場合は, エラーが発生している可能性があるので
チェックをしておくの無難。
App::init() {
// 初期化時に 1 度チェック
// Error は Debug のみ有効にする
assert( glGetError() == GL_NO_ERROR );
}
App::update() {
// 各フレームでも 一度処理をする
assert( glGetError() == GL_NO_ERROR );
}
// もうはいらない.
行列スタックが一杯のときに, glPushMatrix が呼ばれる場合、GL_STACK_OVERFLOW が発生
// もう空っぽ.
行列スタックがひとつのときに, glPopMatrix が呼ばれる場合、GL_STACK_UNDERFLOW が発生.
// ここで補足する
{
static struct Tbl{
int id;
char *msg;
}tbl[]=
{
GL_NO_ERROR, "エラーなし" ,
GL_INVALID_ENUM, "Enum 型の引数が範囲外"
GL_INVALID_VALUE, "数値の引数が範囲外"
// size が同じでない Cube Texture
glTexImage2D( GL_CUBE, 32, 64 )
GL_INVALID_OPERATION, "カレントのステートではできない Command を発行した"
GL_OUT_OF_MEMORY, "コマンドを実行するメモリが不足" ,
GL_STACK_OVERFLOW, "コマンドがスタックのオーバーフローをおこしている"
GL_STACK_UNDERFLOW, "コマンドがスタックのアンダーフローをおこしている"
Stack の Push, Pop がペアになってない
Stack Size の最大数を超えた Push
};
unsigned int err = glGetError();
for( int i=0; i< NROF(tbl); i++ ){
if ( err == tbl[i].id ) {
printf( "ERROR CHECK: %s\n" , tbl[i].msg );
break;
}
}
assert( err == GL_NO_ERROR );
}
■ glGetError
SYNTAX
GLenum glGetError();
DESC
エラーコードを返す。
POINT
glGetError() で取得するまで, 新たな ErrorCode は記録されない。
また, glGetError() で取得すると, エラー状態は GL_NO_ERROR にリセットされる。
glGetError() は glBegin() - glEnd() 内でコールするとエラー扱いになる。( GL_INVALID_OPERATION )
■ gluErrorString
SYNTAX
const GLubyte * gluErrorString( GLenum error);
DESC
GL, GLU エラーコードからエラー文字列を取得する。
■ Error
■ デバッグツール
DESC
OpenGL Command 列 の Capture , State をチェックできる
"OpenGL Debugger" , "OpenGL Tool" で検索すること
■ ErrorCode
DESC
GL_INVALID_ENUM 0x0500 GLenum型引数が範囲外
GL_INVALID_VALUE 0x0501 数値の引数が範囲外
GL_INVALID_OPERATION 0x0502 カレントのステートでの操作が不適切
// StkMtx の問題.
GL_STACK_OVERFLOW 0x0503 コマンドがスタックのオーバーフローを起こしている
GL_STACK_UNDERFLOW 0x0504 コマンドがスタックのアンダーフローを起こしている
GL_OUT_OF_MEMORY 0x0505 コマンドを実行するメモリが不足
■ glBegin(); glEnd(); の不一致
#if 0
glBegin();
...
#endif
glEnd();
■ texture 初期設定の不一致
初期値が環境によって異なる
初期化のときにすべてのパラメータを明示すること
// mipmap を利用しないことを明示する
// ON
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// OFF
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
■ ライティング(Lighting)
SAMPLE
ライティング計算
カメラの移動とライティング計算
ワールド座標指定のライティング
■ Lighting
DESC
ライティング結果は
ライトの設定である glLight() と オブジェクトの材質設定の glMaterial() によって決まる。
計算結果は各頂点の色(プライマリーカラー)となり、
照明がオフの場合は glColor() で指定したカラーが適用される。
テクスチャなどのフラグメント処理は頂点カラーの決定後にされる。
POINT
ライトの位置( 向き )の指定も頂点変換と同じように、モデルビュー変換の影響を受ける。
glLight( GL_POSITION ); をコールした時点で指定した座標が変換される。
常に正面から照らす設定は以下になる。
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
float dir[] = { 0, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, dir );
ワールド座標で指定するにはカメラ座標系から変換する。
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
convertCameraSpace( 0, 0, 500, cy, cx );
// ワールド座標の 0 0 1 の方向から照らす。
float dir[] = { 0, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, dir );
ライティング計算のための初期化をする。
App::onInit() {
// Lighting 処理を有効化( DEFAULT : オフ )
glEnable( GL_LIGHTING );
// ライト数は 少なくとも 8 つ
// 個別に ON/OFF できる。
glEnable( GL_LIGHT0 );
// ライトの位置を設定する
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
float dir [] = { -0.3f, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, dir );
}
描画時は法線( glNormal )を設定したモデルを描画する。
App::onDraw() {
// 法線をもつプリミティブを描画
// 残りの Light の parameter はすべてデフォルトを使う
drawNormalCube();
// WARNING
// Lighting ON は 不要ならばオフにする。
// 以下の 頂点処理に影響をうける
glDisable( GL_LIGHTING );
}
■ ライトの色
ライティングは次の3つの要素で近似する。
環境光 : 間接光
拡散光 : 面に対して均等に拡散する面
鏡面光 : 光沢のある面
ライティングの内部計算。
L : ライト
M : マテリアル
Env * aM +
aL * aM +
dL * dM * ( N.L ) +
sL * sM + ( N.R )
■ 法線(Normal)
DESC
OpenGL は Normal を見て面の向きを決める。
ライティング計算をするためには、面の向きを指定する必要がある。
WARNING
頂点と同じくモデルビュー行列の影響をうけるため Scale 成分の影響をうける。
// Normalize を ON
glEnable( GL_NORMALIZE );
// Scaling のみの調整をする
// Scale Matrix が 等しいなら これで十分
glEnable( GL_RESCALE_NORMAL );
// 最も効率的なのは init 時に normal を再計算すること
recalcNormal();
■ LightParameter
GLInt nr;
glGetIntegerv( GL_MAX_LIGHTS, &nr );
// 各 Light を有効化 ( GL_LIGHTi )
glEnable( GL_LIGHT0 );
// 各 Light の parameter 設定
glLightfv( GL_LIGHT0, GL_DIFFUSE, col );
glLightfv( GL_LIGHT0, GL_POSITION, pos );
■ glLight
SYNTAX
glLight(
GLenum light,
GLenum pramName, // GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_POSITION
TYPE *val // color | position
)
DESC
ライトのパラメータ( 色、位置 )を設定する。
DEFAULT
GL_AMBIENT : 0, 0, 0 // Ambient とは 間接光 ( N 回 Bounce ) の近似
GL_LIGHT0
GL_DIFFUSE : 1, 1, 1
GL_SPECULAR : 1, 1, 1
GL_LIGHT1 - N
GL_DIFFUSE : 0, 0, 0
GL_SPECULAR : 0, 0, 0
DESC
ライトの位置の設定も頂点と同じく、glLight() をコールした時点での
モデルビュー行列変換の影響を受ける。
平行光( DirectionLight )を利用するには, 位置指定のw成分を 0 にする。
無限遠の 0, 0, 1 の位置に置いたことになり 0, 0, -1 方向への平行光として計算される。
// 平行光
float dir [] = { 0, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, dir );
// 位置で指定
float pos [] = { 0, 0, 1, 1 };
glLightfv( GL_LIGHT0, GL_POSITION, pos );
■ glMaterial
SYNTAX
glMaterial(
GLenum face, // 表裏の指定
GLenum pramName, // GL_AMBIENT | GL_DIFFUSE | GL_SPECULAR | GL_SHININESS
const TYPE *val
)
DESC
オブジェクトの色を指定する。
ライトの各成分と乗算される。
POINT
アルファ成分は GL_DIFFUSE で指定したアルファ成分が頂点のアルファ色になる。
GL_AMBIENT, GL_SPECULAR の指定は無視される。
float col[] = { 0, 1, 0, 1 };
glMaterialfv( GL_FRONT, GL_AMBIENT, col );
glMaterialfv( GL_FRONT, GL_DIFFUSE, col );
glMaterialfv( GL_FRONT, GL_SPECULAR, col );
// GL_SHININESS [0:128] DEFAULT 0
glMaterialf( GL_FRONT, GL_SHININESS, 128 );
■ レイトレース(RayTrace)
物が見えるのは、レンズに光(レイ)が飛びこんでくる結果なのでこれを逆からトレースするという考え方。
Direct
1 Bound : 通常のライティングモデル ( )
2 Bound : 照り返し
画素の数だけ、このトレースを繰り返す
2 バウンド
光源からライトがでる ---> 赤い壁にあたる ---> 白い布を照らす ---> カメラのレンズに届く
カメラから画素へ向かってレイをとばす。
何かにあたる
その何かは、全方向からの光の影響をうけるので、全方向にライトをとばす
これを N 階の深さまで繰り返す。
光源方のみに絞ったは 1 バウンドは、直接の光源のみなので、反対領域は黒くなる。
そこで、1バウンドに限定して、暗い部分は適当にバイアスを足しておく( アンビエント )
1バウンドも、ライトという光源方向のみに限定する。
これで画素数だけの計算回数になる。
明るさは 面積あたりの光の粒の数( 密度 )なので、距離の2乗に比例する。
しかし、太陽などの十分に長い距離に対しては、100m などは 0m として近似できるため
距離の減衰を無視できる。
反射の割合( 分布 )
ある方向からきた光は、必ず一定方向にだけ反射するわけではない。
そうだとしたら見る方向をかえると、黒く見えてしまう。
そこで、全方向に一様に反射すると仮定する。
これを計算式として表現すると、ディフューズモデルができる
I = I0 * R * cos(theta), cos(theta) = dot(L , N)
L : 頂点から光源へのベクトル
N : 頂点の法線ベクトル
I0 : 光源の色
R : 反射する割合
■ ライティング計算を自前でする
OpenGL にはライティング計算をするための API があるが
自前で計算式から明るさを求めて、頂点カラーとして渡すこともできる。
テクスチャを貼るときは、 GL_MODULATE をつかえばいい。
REFERENCE テクスチャのブレンド方法を指定する
■ PipeLine
POINT
大まかには 頂点処理, Fragment 処理にわかれる
[ VertexTransform ]
|
[ FragmentTest ] // FrameBuffer に入る前に Test
TIP
Triangle 単位で Pipeline が進行する.
ひとつの DrawCall 内でも, DepthTest, DepthWrite が有効になる
■ Fragment ごとの処理
次の順番で処理される
// Application が定義した Window 座標の外の領域の Fragment を破棄
1: シザー
// Application で指定した Alpha 値より Fragment の Alpha 値が小さいなら破棄
2: アルファ
// FrameBuffer の Stencil 値と比較して 一定の処理をする
3: ステンシル
// Fragment の Depth 値と FrameBuffer の Depth 値と比較して 小さければ破棄
4: デプス
//
5: 混合処理
6: ディザ
7: 論理
■ シェーダ(Shader)
■ shader の対応について
DESC
glview によれば ...
Vertex Program extension found.( DX8 Vertex Shader )
Fragment Program extension found.( DX9 Pixel Shader )
No OpenGL Shading Language support( GLSL は不可 )
■ 基本的なれ流
SAMPLE
フラグメントシェーダ
#include "Cg/cg.h"
#include "Cg/cgGL.h"
#pragma comment (lib, "cg.lib" )
#pragma comment (lib, "cgGL.lib" )
App::init() {
CGprofile pfV = CG_PROFILE_ARBVP1;
CGprofile pfF = CG_PROFILE_ARBFP1;
CGprogram shdV, shdF;
CGcontext ctx = cgCreateContext();
// Shader は使えるか確認
cgGLIsProfileSupported();
// shader code を読んで Program を作成
shdV = cgCreateProgramFromFile( ctx, CG_SOURCE, ".\\vertex.cg" , pfV, NULL, NULL);
shdF = cgCreateProgramFromFile( ctx, CG_SOURCE, ".\\fragment.cg" , pfF, NULL, NULL);
// または文字列バッファから作成
shdF = cgCreateProgram( ctx, CG_SOURCE, "..." , pfF, NULL, NULL);
// あげる
cgGLLoadProgram(shdV);
cgGLLoadProgram(shdF);
// Shader 有効化
cgGLEnableProfile( pfV );
cgGLEnableProfile( pfF );
cgGLBindProgram( shdV );
cgGLBindProgram( shdF );
}
App::draw() {
// Shader の指定
// Texture Object の指定と同じ
cgGLBindProgram();
// 描画
draw();
}
フラグメントの色を単色にするだけのフラグメントシェーダ
float4 main() : COLOR
{
return float4( 0, 0, 1, 1 );
}
■ cgGLEnableProfile
SYNTAX
void cgGLEnableProfile( CGprofile profile )
DESC
プロファイルを有効化する。
有効化した後の OpenGL の描画コールではシェーダが利用される。
■ cgGLDisableProfile
SYNTAX
void cgGLDisableProfile( CGprofile profile )
// 固定機能の描画に戻す。
cgGLDisableProfile( pfV );
cgGLDisableProfile( pfF );
// 描画
drawCube();
■ 頂点シェーダ
SAMPLE
頂点シェーダ
とりあえず、固定機能と同じように描画してみる。
App::onDraw() {
// 変換行列を設定。
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glFrustum();
// シェーダから変数 "mvp" のハンドルを取得する。
CGparameter param = cgGetNamedParameter( shdV, "mvp" );
// OpenGL のステートである ModelViewProjection 行列をセットする。
// これをコールした時点の ModelViewProjection 行列がセットされるため
// 先に行列を設定しておく必要がある。
cgGLSetStateMatrixParameter( param,
CG_GL_MODELVIEW_PROJECTION_MATRIX,
CG_GL_MATRIX_IDENTITY );
// この設定でプリミティブを描画
drawCube();
}
頂点シェーダ
struct app {
float4 pos : POSITION; // 頂点データ
};
struct v2f {
float4 pos : POSITION; // 頂点データ
};
v2f main(
app IN ,
uniform float4x4 mvp
) {
v2f OUT ;
// 頂点を変換してフラグメントシェーダへ渡す。
OUT .pos = mul( mvp, IN .pos );
return OUT ;
}
頂点シェーダ側では任意の頂点アトリビュートをフラグメントシェーダへレジスタ経由で渡す
ことができる。
法線の値をフラグメントの色に指定してみる。
struct app {
float4 pos : POSITION; // 頂点データ
float4 nrm : NORMAL; // 法線データ
};
struct v2f {
float4 pos : POSITION; // 頂点データ
float4 nrm : TEXCOORD0; // フラグメントシェーダへ法線の値を渡す。
};
v2f main(
app IN ,
uniform float4x4 mvp
) {
v2f OUT ;
// 頂点を変換してフラグメントシェーダへ渡す。
OUT .pos = mul( mvp, IN .pos );
OUT .nrm = IN .nrm;
return OUT ;
}
フラグメントシェーダでは頂点シェーダから渡された法線の値を出力する。
float4 main( v2f IN ) : COLOR
{
return float4( 0.5f * IN .nrm.xyz + 0.5f, 1 );
}
■ フラグメントシェーダからテクスチャをサンプルする
SAMPLE
シェーダへテクスチャをわたす
サンプラー変数のハンドルを取得して、
利用したいテクスチャオブジェクトの id を指定する。
App::onInit() {
glGenTextures( 1, &id );
}
App::onDraw() {
CGparameter p = cgGetNamedParameter( shdF, "decal" );
cgGLSetTextureParameter( p, id );
cgGLEnableTextureParameter( p );
}
フラグメントシェーダではサンプラー型の変数を宣言しておく
float4 main(
v2f IN ,
uniform sampler2D decal
) : COLOR
{
return tex2D( decal, IN .tc.xy );
}
■ cgGLSetTextureParameter
SYNTAX
void cgGLSetTextureParameter(
CGparameter param,
GLuint texobj
);
DESC
param で指定したテクスチャユニットにテクスチャオブジェクトをバインドする。
glBindTexture() に相当する処理。
■ cgGLEnableTextureParameter
SYNTAX
cgGLEnableTextureParameter( CGparameter )
DESC
指定したテクスチャパラメータ(ユニット == サンプラー)を有効化する。
この処理は, テクスチャを利用して描画する前にコールしておく必要がある。
とあったが, あってもなくても動く?
テクスチャの適用を解除するときは cgGLDisableTextureParameter() をコールする。
■ フラグメント単位のライティング
SAMPLE
フラグメント単位のライティング
アプリケーション側では視線の位置を頂点シェーダにわたす。
{
CGparameter param = cgGetNamedParameter( shdV, "eyepos" );
cgGLSetStateMatrixParameter( param,
CG_GL_MODELVIEW_MATRIX,
CG_GL_MATRIX_INVERSE
);
}
頂点シェーダ側ではフラグメントシェーダに渡す。
uniform float4x4 eyepos;
OUT .nrm = IN .nrm;
OUT .E = float4( eyepos[0][3],eyepos[1][3],eyepos[2][3],eyepos[3][3] ) - IN .pos;
頂点シェーダから視線ベクトルと法線ベクトルをフラグメントシェーダへ渡す。
フラグメントでのそれらの値は頂点間で線形補間されるため、 Normalize() が必要。
struct v2f {
// 頂点データ
float4 pos : POSITION;
float4 nrm : TEXCOORD0;
float4 E : TEXCOORD3; // 視線ベクトル
};
float3 E = normalize( IN .E.xyz );
float3 N = normalize( IN .nrm.xyz );
// ライトベクトル
float3 L = float3( 0, 0, 1 );
// ハーフベクトル
float3 H = normalize( (L + E)/2 );
// ディフューズ成分
float d = max( dot( L, N ), 0 );
// スペキュラー成分
float s = max( 0, pow( dot( H, N ), 30 ) );
d += s;
return float4( d, d, d, 1 );
ToonShader はライティング結果を階調化して表現してみる。
s = step( 0.5, s );
float n = 3;
d = floor ( d * n ) / n;
d += s;
return float4( d, d, d, 1 );
WARNING
頂点シェーダで受け取る頂点属性は必ずアプリケーション側から渡すこと。
struct app {
float4 pos : POSITION;
float4 nrm : NORMAL;
float2 tc : TEXCOORD0;
};
glBegin( GL_TRIANGLES );
// この2つは必須
glNormal3f( 0, 1, 0 );
glTexCoord2f( 0, 0 );
glVertex3f();
glVertex3f();
glVertex3f();
glEnd();
■ ノーマルマップ(NormalMap)
SAMPLE
ノーマルマップ
ノーマルマップとライティング
カラーのテクスチャからノーマルマップに変換する
float3 calcNormalMap( sampler2D decal, float2 tc)
{
// 1テクセルの大きさ
// 1024×1024のテクスチャを利用する場合
float du = 1 / 1024.0f;
float dv = 1 / 1024.0f;
// 隣接テクセルの値
float2 tx = tc + float2( du, 0 );
float2 ty = tc + float2( 0, dv );
// 元のテクスチャがカラーのため、明度のみの色に変更
float cx = convMono( tex2D( decal, tx ).xyz );
float cy = convMono( tex2D( decal, ty ).xyz );
tx = tc - float2( du, 0 );
float cx2 = convMono( tex2D( decal, tx ).xyz );
ty = tc - float2( 0, dv );
float cy2 = convMono( tex2D( decal, ty ).xyz );
float scl = 1;
// X, Y 方向の傾きを計算する
float3 vu = normalize( float3( 1, 0, scl*(cx - cx2) ) );
float3 vv = normalize( float3( 0, 1, scl*(cy - cy2) ) );
// 2つの軸に直交するz軸を計算する
float3 N = cross( vu, vv );
return N;
}
float4 main(
v2f IN ,
uniform sampler2D decal
) : COLOR
{
// -1 : 1 の範囲の値を 0 : 1 に変換する
return float4( 0.5f * calcNormalMap( decal, IN .tc.xy) + 0.5f, 1 );
}
■ ポストエフェクト(PostEffect)
SAMPLE
ポストエフェクト モザイク
ポストエフェクト エッジ抽出
ポストエフェクト ぼかしを加算
ポストエフェクトをするには、描画した画面をフラグメントシェーダを利用して
再処理をする。
// 適当にシーンを描画
drawScene();
// 画面をテクスチャへコピー
glBindTexture( GL_TEXTURE_2D, id );
glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
0, 0, W, H, 0 );
// ポストエフェクト用の頂点シェーダとフラグメントシェーダをバインドする。
cgGLBindProgram( shdV );
cgGLBindProgram( shdF );
// フラグメントシェーダで利用するテクスチャを渡す。
CGparameter p = cgGetNamedParameter( shdF2, "decal" );
cgGLSetTextureParameter( p, id );
cgGLEnableTextureParameter( p );
// テクスチャを画面を覆う四画形ポリゴンにはりつけて描画する。
glBegin( GL_QUADS );
glTexCoord2f( 0, 0 );
glVertex3f( -1, -1, z );
glTexCoord2f( 1, 0 );
glVertex3f( 1, -1, z );
glTexCoord2f( 1, 1 );
glVertex3f( 1, 1, z );
glTexCoord2f( 0, 1 );
glVertex3f( -1, 1, z );
glEnd();
頂点シェーダでは正規化デバイス座標系で -1 : 1 の範囲を覆うように変換しておく。
四画形の各頂点を -1 1 にしておき、モデルビュー変換、プロジェクション変換をしないでスルーさせる。
struct app {
// 頂点データ
float4 pos : POSITION;
float4 tc : TEXCOORD0;
};
struct v2f {
float4 pos : POSITION;
float4 tc : TEXCOORD1;
};
v2f main(
app IN
)
{
v2f OUT ;
// -1 1 の範囲になるようにスルーさせる。
OUT .pos = float4( IN .pos.xy, -0.9f, 1 );
OUT .tc = IN .tc;
return OUT ;
}
フラグメントシェーダでは描画した画面に対して適当な処理をする。
float4 main(
v2f IN ,
uniform sampler2D decal
) : COLOR
{
return float4( tex2D( decal, floor( IN .tc.xy * 100 ) / 100.0f ).xyz, 1 );
}
■ 基本事項
■ GL の原則
一度設定した 内容は 変更されるまで有効
State を保存する場所を Rendering Context という
WARNING
State の状態を正しく管理しないと, 想定しない動作に悩まされる
以下の方法をとる
init 時に 頻繁に利用する State にする
State を変更するときは 属性 Stack ( Push, Pop ) で囲む
■ 描画命令の順
DESC
Command の発行順に描画される
Primitive 単位でも同じこと
glDrawElements();
clearcolor -> drawtri
drawtri -> clearcolor では 上塗りされる
TIP
matrix の順番も特に指定する必要なし( 現在のstate が描画時に反映 )
描画時とは begin() - end() の間
glMatrixMode( MV );
glMatrixMode( PROJ );
draw
glMatrixMode( PROJ );
glMatrixMode( MV );
draw
glMatrixMode( PROJ ); // これは既に有効ではない
透明に関しては以下の3種類
1. 不透明( 手前から, 塗りを減らすため )
3. クリップ( 奥から描画, αテストの部分が補間の関係でマージンができる為 )
2. 半透明( 奥から描画 )
■ glPushAttrib
SYNTAX
void glPushAttrib( GLbitfield );
DESC
指定した Group の属性を Stack に Push
利用してみたが, かなり効果的
1 Primitive 描画毎に Push, Pop してもいい
// 全部 保存
glPushAttrb( GL_ALL_ATTRIB_BITS );
■ glPopAttrib
■ glPushClientAttrib
■ glPopClientAttrib
■ State の設定 クエリ
■ glIsEnabled()
SYNTAX
bool glIsEnabled(
GLenum val
);
DESC
test whether a capability is enabled
■ glGet
DESC
OpenGL の実装依存の情報を取得する
( init 時にする )
問い合わせる型にあった関数を利用する
float m[16];
glGetFloatv( GL_MODELVIEW_MATRIX, m );
■ Stack を使って管理する
State の変更を管理するための stack( 属性スタック ) が Server, Client 側にある
■ glPushAttrb
SYNTAX
glPushAttrb(
GLbitfield mask
)
glPopAttrb();
// Material, Light 属性のみを Push する
glPushAttrb( GL_LIGHTING_BIT );
// そして元に戻す
glPopAttrb();
// Stack のサイズをしらべる
int nr;
glGetIntegerv( GL_MAX_ATTRIB_STACK_DEPTH, &nr );
■ 開発環境
以下の Header, library が必要
OpenGL Header, library
Window System の Header, library
visualstudio.net には OpenGL 1.1 の Header と Export StubLibrary がふくまれる
// version 1.1 以降の機能, 拡張機能 を利用するには glext.h が必要
#include "GL/glext.h"
Microsoft Compiler は glu1.2 の header, StubLibrary を含む
■ 用語
■ Primitive
DESC
Primitive とは 1 つ以上の 頂点のグループ
線の Primitive は 2 つ以上の頂点が必要。
プリミティブは頂点を組み立てることで生成される。
■ Profile(プロファイル)
DESC
自分の OpenGL 環境をしる
■ GL version
GL のバージョンとは固有の機能拡張 == 新API の追加のこと
機能拡張は各vendor からリリースされている
2.0対応( OpenGL Extensions Viewerで確認 )
2.0対応ということは2.0の拡張機能を利用しているということ
表示可能な画面解像度も備えている
■ glGetString
DESC
OpenGL の実装情報を 文字列でえる
init 時に GL_VERSION, GL_EXTENSIONS をしらべて support する機能をしらべる
glEnable() < -> glIsEnable()
glGetXXX() : state 値の問い合わせ( perf が必要な部分での使用は禁止 )
bool profile() {
bool ret = false;
version = (char *)glGetString( GL_VERSION );
printf( "GL_EXTENSIONS: %s\n" , glGetString( GL_EXTENSIONS ) );
printf( "GL_VERSION: %s\n" , glGetString( GL_VERSION ) );
printf( "GL_REBDER: %s\n" , glGetString( GL_RENDERER ) );
printf( "GL_VENDOR: %s\n" , glGetString( GL_VENDOR ) );
return ret;
}
DESC
openGL は [ call した順 ]にすべての処理がされる( 描画順も制御可能 )
1. .ピクセル系の処理
glReadBufferやglReadPixelなどの処理は負荷が高い
VRAM ではなく MEMORY に転送するので
■ cgGl
■ cgDestroyEffect
SYNTAX
void cgDestroyEffect( CGeffect effect );
DESC
Effect object を破棄する
Effect を破棄すると それに関連する Techinique Pass も無効になる
■ cgIsEffect
SYNTAX
CGbool cgIsEffect( CGeffect effect );
DESC
determine if an effect handle references a valid effect
■ cgGetLastListing
SYNTAX
const char * cgGetLastListing( CGcontext context );
RET
NULL: 空だった
DESC
get the current listing text
Cg Compiler , CG_ERROR の情報が取得できる
cgGetLastListing();
POINT
Context には Error 情報とかもはいっている
■ cgGetFirstStateAssignment
SYNTAX
cgGetFirstStateAssignment( CGpass pass )
DESC
get the first state assignment in a pass
// 描画 pass
Pass {
State
}
POINT
Nest 構造をたどっていく
■ cgCreateProgramFromFile
SYNTAX
cgCreateProgramFromFile
DESC
指定した cg ソースコード, コンパイル済みのオブジェクトから CgProgram を生成する。
// 相対パスで指定する
shdV = cgCreateProgramFromFile( ctx, CG_SOURCE, "data/shader/test.vsh" , pf, NULL, NULL);
// 絶対パスで指定する
shdF = cgCreateProgramFromFile( ctx, CG_SOURCE, "d:/data/shader/test.vsh" , pf, NULL, NULL);
■ エラーチェック
■ cgGetError
SYNTAX
CGerror cgGetError( void );
DESC
エラーステートを取得する。
cgGetError() をコールするとエラー状態はリセットされる。
CGerror err = cgGetError();
assert( err == CG_NO_ERROR );
■ cgSetErrorCallback
SYNTAX
typedef void (*CGerrorCallbackFunc)( void );
void cgSetErrorCallback( CGerrorCallbackFunc func );
DESC
Cg ランタイムエラーが発生した時にコールバックされる関数を設定する。
void MyErrorCallback() {
const char* s = cgGetErrorString( cgGetError() );
printf( "ERROR: Cg Runtime msg = %s\n" , s );
}
cgSetErrorCallback( MyErrorCallback );
■ cgGetErrorString
SYNTAX
const char * cgGetErrorString( CGerror error );
DESC
エラーコード値からエラー文字列を返す。
■ cgGetNamedParameterAnnotation
SYNTAX
CGannotation cgGetNamedParameterAnnotation( CGparameter param,const char * name );
RET
NULL : なかった
CG_INVALID_PARAM_HANDLE_ERROR is generated if param is not a valid parameter.
DESC
名前, parameter を指定して CGannotation をえる
■ cgGetFirstTechnique
SYNTAX
CGtechnique cgGetFirstTechnique( CGeffect effect );
RET
CG_INVALID_EFFECT_HANDLE_ERROR( if effect is not a valid effect )
DESC
Effect の最初の Techinique をえる
// effect 内の すべての Technique を Iterate するときに使う
CGeffect effect = cgCreateEffectFromFile(context, cgfx_filename, NULL);
CGtechnique technique = cgGetFirstTechnique(effect);
while (technique) {
// Do something with each technique
// 有効かを調べる
// CGbool cgValidateTechnique( CGtechnique tech );
if ( cgValidateTechnique(tech) ) {
// 名前をえる
printf("ERR: %s" , cgGetTechniqueName(tech) );
}
// 次の Tech へ
technique = cgGetNextTechnique(technique);
}
■ cgGetNamedParameter
SYNTAX
CGparameter cgGetNamedParameter( CGprogram, const char *name )
DESC
指定 shader Program から uniform data へのハンドルを取得
RET
null : 指定した Parameter がない
!null : ParameterHandle
TIP
RetVal で Shader 内に, どの UniParam があるか確認可能.
// Vertex Program から ModelViewProj を取得してセット
param = cgGetNamedParameter( vp, "ModelViewProj" )
Matrix m;
cgGLSetMatrixParameterfr( param, m.f );
■ cgGLSetMatrixParameter
SYNTAX
DESC
uniform 型行列( 4 X 4 行列 )パラメータの設定をする。
r 接尾辞
行優先の順序で配置されていることを想定
c 接尾辞
行列が列優先の順序で配置されていることを想定する
■ cgGLSetStateMatrixParameter
SYNTAX
void cgGLSetStateMatrixParameter(
CGparameter param,
GLenum type, // 行列の種類
GLenum transform // 追加の変換処理
);
DESC
OpenGL の行列を直接わたす。
POINT
列, 行の指定はなく、Cg 側では列優先として行列が設定される。
つまり、位置を取得するには 第3列の値をとる。
cgGLSetStateMatrixParameter(
param,
CG_GL_MODELVIEW_MATRIX,
CG_GL_MATRIX_INVERSE
);
uniform float4x4 eyepos;
// 視線ベクトル
E = float4( eyepos[0][3], eyepos[1][3], eyepos[2][3], eyepos[3][3] ) - IN .pos;
合成行列 CG_GL_MODELVIEW_PROJECTION_MATRIX
モデルビュー行列 CG_GL_MODELVIEW_MATRIX
射影行列 CG_GL_PROJECTION_MATRIX
現在のテクスチャ CG_GL_TEXTURE_MATRIX
パラメータ値の設定に使用する前にステートの行列に適用する変換を指定
変換を適用しない CG_GL_MATRIX_IDENTITY
行列を転置する CG_GL_MATRIX_TRANSPOSE
行列を逆にする CG_GL_MATRIX_INVERSE
行列に逆と転置の両方する CG_GL_MATRIX_INVERSE_TRANSPOSE
cgGLGetParameter1f(CGparameter parameter, float* array);
cgGLGetParameter1d(CGparameter parameter, double* array);
cgGLGetParameter2f(CGparameter parameter, float* array);
cgGLGetParameter2d(CGparameter parameter, double* array);
cgGLGetParameter3f(CGparameter parameter, float* array);
cgGLGetParameter3d(CGparameter parameter, double* array);
cgGLGetParameter4f(CGparameter parameter, double* array);
cgGLGetParameter4d(CGparameter parameter, type* array);
void cgGLSetMatrixParameterfr(CGparameter parameter,
const float* matrix);
void cgGLSetMatrixParameterfc(CGparameter parameter,
const float* matrix);
void cgGLSetMatrixParameterdr(CGparameter parameter,
const double* matrix);
void cgGLSetMatrixParameterdc(CGparameter parameter,
const double* matrix);
void cgGLSetMatrixParameterArray{fd}{rc}( CGparameter param,
long offset,
long nelements,
const TYPE * v );
cgGLSetParameterArray
■ パラメータ(parameter)
■ cgGLSetParameterArray4
SYNTAX
cgGLSetParameterArray4(
CGparameter param,
long offset, // 配列 v の開始オフセット( 先頭からなら 0 )
long nr, // float4 型配列の要素数
const float * v // データ
);
DESC
配列型パラメータに値をセットする。
// スキニング行列を設定する。
float m[] = {
1, 0, 0, 200,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
1, 0, 0, -400,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
}
param = cgGetNamedParameter( shdV, "m" );
cgGLSetParameterArray4f( param, 0, 2*4, m );
v2f main(
app IN ,
uniform float4 m[4*2]
)
{
float4x4 mtx = float4x4( m[0],
m[1],
m[2],
m[3] );
float4 pos = mul( mtx, IN .pos );
}
■ cgGLSetParameter4fv
SYNTAX
void cgGetNamedParameter( CGparameter, float *)
DESC
float N 型値をセットする
■ cgGLSetParameter
SYNTAX
cgGLSetParameter
CGparameter p = cgGetNamedParameter( db.createShd( "pe_dof.fsh" ), "testparam" );
cgGLSetParameter4f( p, 1, 1, 0, 1 );
■ cgGetArraySize
SYNTAX
int cgGetArrayDimension(CGparameter parameter);
int cgGetArraySize( CGparameter p, int d );
CGparameter cgGetArrayParameter(CGparameter p, int idx );
DESC
case CG_ARRAY:
{
// 0 次元目の 要素数 をかえす
//
//
//
//
int arraySize = cgGetArraySize(p, 0);
myas( 0, "not impl Array size %d" , arraySize );
■ Cg
■ Profile.別性能表
DESC
Profile とは Graphics 命令セットの種類のこと
C, C++ で Target Machine を指定するのと同じ
Cg Compiler にこの Hardware 用に Compile しろというのと同じ
WARNING
GPU によって利用できる命令セット, インストラクション数に限界がある
コンパイルができても Load で失敗する可能性がある
// foo.fsh(133) : error C5051: profile does not support conditional returns
cgc -arbfp1 foo.fsh
// vertex shader は tex2D 命令は support しない
// foo.fsh(176) : error C3004: function "tex2D" not supported in this profile
cgc -arbvp1 foo.fsh
■ 行列の積(mul)
POINT
Cg の mul は次の順番で処理される。
mul( m, v ) は以下の計算結果になる。
m00 m01 m02 vx m00*vx + m01*vy + m02*vz
m10 m11 m12 vy == m10*vx + m11*vy + m12*vz
m20 m21 m22 vz m20*vx + m21*vy + m22*vz
WARNING
// 可換ではない
mul( v, m ) != mul( m, v )
// Fragment Shader で確認できる
float3x3 m = {
{ 1, 1, 1 },
{ 0, 0, 0 },
{ 0, 0, 0 },
};
{
float3x3 m = {
{ 1, 0, 0 },
{ 1, 0, 0 },
{ 1, 0, 0 },
};
// 結果は白 ( 1, 1, 1 )になる。
return float4( mul( m, float3(1,1,1) ), 1 );
}
行列の変換がよく理解できなくなるため, 正解をかいておく
Application::Mtx -> transpose -> Mul( mtx, vtx );
Model -> World -> View -> Projection
mul(m, IN .pos) mul(v, posW) mul( p, posV )
法線の変換
POINT
N, T の関係から N の変換行列を求める。
T の変換は 位置の変換と同じ処理をする
N.T = 0;
Nw.Tw = Nw . (W*Tl) = (Wt * Nw) . Tl
(a)bc (Nx) (Tl)
(a)bc (Ny) = (Tl)
(a)bc (Nz) (Tl)
Nw . ( W[0].Tx, W[1].Ty, W[2].Tz )
( Nx, Ny, Nz ) . ( W[0].Tx, W[1].Ty, W[2].Tz )
Nx には W[0] の成分がかかるから
左から 行列をかけるときは 転置して W[0] を縦に配置する
Wt*Nw = Nl が成り立つ ( )
// 転置の逆行列
Nw = (Wt)-1 * Nl
■ shader.コツ
Constant Register 数に気をつける
Matrix の積ではなく, dot ( 内積 )で代用する
命令数に気をつける
■ SYNTAX
POINT
global 変数として定義しても良い。
この方が, 関数の引数よりもテストしやすいので利用すること
// インクルード
#include "common.h"
// global として定義
uniform float scl = 1;
float4 main(
// 仮引数として定義
uniform float scl;
)
マクロも使える
// #define
#define LIGHT_NUM 7
#ifdef _MAYA_
#else
#endif
// 引き数つきマクロ
#define LIGHT_DECLARE( N ) float4 light_ ## N
// C と同じく \ で複数行を連結する。
#define LIGHT_DECLARE( N ) float normalmap_scale ## N \
< \
UIName = "Nomalmap Scale" \
> = {0.1f}
■ 行列
// 成分のとり方
float3 v = m._m03_m13_m23;
// 1行目を取得。
float3 v = m[0];
Cg 内の Matrix と 成分を調査
守ること.
Application -> Cg に渡す際に transpose すること
かける順番
mul( m, v );
次のように構成だと考えられる
Ax Ay Az T
[00][01][02][03] (x) = (Xnew + Tx)
[10][11][12][13] (y) (yNew + Ty)
[20][21][22][23] (z) (zNew + Tz)
[30][31][32][33] (1) (1)
POINT
mul( m, v )は 次の計算
v.x = 0行目 ( m[0] ) * v
v.y = 1行目 ( m[1] ) * v
v.z = 2行目 ( m[2] ) * v
v.w = 3行目 ( m[3] ) * v
[] 演算子は行列の行を取得する。
次の計算で確認できる。
float4x4 m;
m[0] = float4(1,1,0,0);
m[1] = float4(0,1,0,0);
m[2] = float4(0,0,1,0);
m[3] = float4(0,0,0,1);
// { 1, 0.5, 0, 1 } == オレンジ色
float s = 0.5f;
OUT .col = mul( m, float4(s,s,0,1) );
// { 0.5, 1, 0, 1 } == ライトグリーン
m[0] = float4(1,0,0,0);
m[1] = float4(1,1,0,0);
m[2] = float4(0,0,1,0);
m[3] = float4(0,0,0,1);
OUT .col = mul( m, float4(s,s,0,1) );
// 成分指定 がどこを指すかチェック
m[0] = float4(1,0,0,0);
m[1] = float4(1,1,0,0);
m[2] = float4(0,0,1,0);
m[3] = float4(0,0,0,1);
m._m10 = 0; // 1行, 0 列
m._m11 = 0;
// { 0.5, 0, 0, 1 } Dark Red
float s = 0.5f;
OUT .col = mul( m, float4(s,s,0,1) );
行列スウィズル演算子
< type>< rows>x< columns> という形式の行列型の場合、
matrix._m< row>< col>
matrix._m< row>< col>[_m< row>< col>][...]
という表記法を使用して、個々の行列要素にアクセスしたり(< row>< col>ペアが1つのみの場合)
行列の要素からベクトルを構成したり(< row>< col>ペアが複数存在する場合)することができる。
行と列の番号は0 から始まる
// 連続で指定できる
// Assign the main diagonal of myMatrix to myFloatVec4.
fvec4 = myMatrix.m_00_m11_m22_m33;
float4x4 a と宣言したとすると、a[3] は a._m30_m31_m32_m33 に等しくなる
どちらの式も、行列の3 行目を取り出す
// Az 成分( 列 )をとる
float3 Az = m._m02_m12_m22;
// Cg Reference より抜粋
mul() は 3 つの使用方法がある。
行列 × 列ベクトル
mul(M, v);
行ベクトル × 行列
mul(v, M);
行列 × 行列
mul(M, N);
// 次のような 記述になる
(m11 m21 m31 m41) (v1)
(m12 m22 m32 m42) (v2)
(m13 m23 m33 m43) (v3)
(m14 m24 m34 m44) (v4)
float3 eyevec_w = viewI._m03_m13_m23 - pos_w;
OUT .test = float4( viewI._m03_m13_m23, 1.0f );
float3x3 nmatrix = float3x3( tan, bin, nml );
nmatrix[0] = tan;
nmatrix[1] = bin;
nmatrix[2] = nml;
float4 a = float4( 1.236812, -0.714074, -0.866025, -0.577350 );
float4 b = float4( 0.000000, 1.428148, -0.866025, -0.577350 );
float4 c = float4( -1.236812, -0.714074, -0.866025, -0.577350 );
float4 d = float4( 1.785185, 0.507938, -68.817703, 120.788200 );
// float4 から matrix 生成
float4x4 mtxLit = float4x4( a, b, c, d );
float4 a = float4(1,2,3,1);
return float4(a[0], a[1], a[2], 1 );
// 要素をえる( 0 行 0 列 )
float4x4 m;
float f = m[0][0];
■ Reference(Cg)
■ floor
SYNTAX
floor( f )
DESC
f 以下の最大の整数
浮動小数点 の 整数部 をとりだす
■ frac
SYNTAX
frac( f )
DESC
f の小数部
■ cross
SYNTAX
cross(float3 a, float3 b)
DESC
ベクトル a と b の外積
a から b への外積
a = 1, 0, 0
b = 0, 1, 0
0 0 1
0 0 1
-----
0 0 1
0 1 0
1 0 0
-----
0 0 0
1 0 0
0 1 0
-----
0 0 1
0 0 1
------
-1
c = a * b の定義 ( aからbまで回す右ねじの進む向き)
a.y*b.z + a.z * b.x + a.x + b.y
-
b.y*a.z + b.z*a.x + + b.x*a.y
float3 cross(float3 a, float3 b)
{
return a.yzx * b.zxy - a.zxy * b.yzx;
}
// fragment shader で試すと 青
return float4( cross( float3(1,0,0), float3(0,1,0) ), 1 );
// 黒
return float4( cross( float3(0,1,0), float3(1,0,0) ), 1 );
■ lerp
SYNTAX
lerp(a, b, f)
DESC
(1-f)*a + f*b
TIP
WARNING
描画結果が正しくない現象が見られる
自前で計算した方が無難
■ reflect
SYNTAX
floatN reflect( floatN i, floatN n);
DESC
param n: Normal ( 要 normalize )
param i: IncidentVector
WARNING
i は VtxPos - EyePos
Lighting 時とは逆向き の Vector
PROFILE
fp20
■ tex2Dproj
SYNTAX
tex2Dproj (sampler2D tex, float4 sq);
DESC
Texture 座標を W=1 の空間に変換後に sampling する
PROFILE
PS20
シャドウマップルックアップ:
テクスチャ座標のz 成分が、シャドウマップと比較する深度値を保持
シャドウマップルックアップでは
深度比較テクスチャリングのためにアプリケーションにより構成された関連テクスチャのユニットが必要
これがないと、深度比較は実際に実行されない
■ clamp
SYNTAX
clamp(x, a, b)
DESC
x < a = x = a
x > b = x = b
else x;
■ abs
SYNTAX
abs(x)
DESC
abs(x) x の絶対値
■ length
SYNTAX
float length( floatN vec )
DESC
vec の長さ( ノルム )を返す
float length( float3 vec ) {
return sqrt( dot(vec, vec) );
}
■ exp
SYNTAX
exp(x)
DESC
Euler ^ x
■ exp2
SYNTAX
exp2(x)
DESC
2^x
■ step
SYNTAX
floatN step( floatN a, floatN x)
DESC
x >= a となる階段関数で変換した値を返す。
■ normalize
SYNTAX
normalize(x)
DESC
N次元ベクトルを正規化する
■ 高速化
■ 頂点共有をする
GL_LINE_LOOP
GL_LINE_STRIP
頂点を共有するため, 頂点変換の Pipeline を軽減できる
GL_QUADS : 8 頂点
GL_QUAD_STRIP : 6 頂点
■ グラフィックカードのメモリを利用する
グラフィック用のハードは専用のメモリをもつ。
メインメモリからグラフィック用のメモリへの転送はとても時間がかかるので
初期化のタイミングでグラフィックメモリに形状、テクスチャを転送しておく。
REFERENCE 頂点バッファオブジェクト
■ GPUの終了まちを避ける
GPU と CPU は独立したハードウェアのため、CPU から GPU へ投げた仕事を
CPU 側が待つ時間を有効に使う。
■ ライティングを高速化する
Cg シェーダを使うときは 、コンパイルしたアセンブラをみて、命令数をチェックしてみる。
ライティングについては、各頂点をワールド、視線空間へ変換せずにローカル座標にもっていく。
こうすると、 N 頂点の変換ではなく、1 個のライトベクトルの変換ですむ。
■ デプステスト(DepthTest)
■ DepthBuffer(デプスバッファ)
DESC
OpenGL は DepthBuffer による陰面消去アルゴリズムをサポートする。
DepthBuffer とは画素ごとに Z 座標を記録する領域のこと。
利用するには Window を作成時に DepthBuffer を用意する
Window 空間での z 値より小さい Fragment は破棄される
Application が DepthTest を OFF にすると
後でかいた Primitive によって上書きされる
DEFAULT
OFF
h
// ON ( DEFAULT: OFF )
glEnable( GL_DEPTH_TEST );
// Detph Test を指定 ( DEFAULT: GL_LESS )
glDepthFunc( GL_LEQUAL );
Z 値の変換式
■ 半透明の描画
■ Performance ■ FrameBuffer
■ 画面をキャプチャする
glReadPixels() を利用して画面をClientMemoryにコピーする
void App::draw() {
// 何か FrameBuffer に描く
draw();
// 適当なイベントで実行する
if ( mouseEvent )
{
const int sz = 800 * 600 * 4;
byte[] buf = new byte[ sz ];
// サイズ 800, 600 の画面を buf へコピーする
glReadPixels( 0, 0, 800, 600, GL_RGBA, GL_UNSIGNED_BYTE, buf );
// Fileに出力する
FILE *fp = fopen( "capture.raw" , "wb" );
fwrite( buf, sz, 1, fp );
fclose(fp);
delete[] buf;
}
}
■ glReadPixels
SYNTAX
void glReadPixels(
int x, int y //読み取る領域の左下隅の Window空間の x, y座標
unsigned int width, height //読み取る領域の幅, 高さ
GL_BGRA, //取得したい色情報の形式()
GL_UNSIGNED_BYTE, //読み取ったデータを保存する配列の型
void *buf //データを保存する配列へのポインタ
);
DESC
Framebuffer の内容をクライアントメモリへ Copy する
WARNING
この処理は、かなりの負荷がかかる
Default は BackBuffer から得る
この Command は 終了するまで制御を返さない
そして GL の Pipeline をすべて Flush する
App::init() {
//画像(読み取る領域)のピクセル数
int nr = w * h;
// 1 Pixel 要素数 ( RGB 3 RGBA 4 )
int format = 4; //rgba
// バッファから読み取ったデータを格納する配列
byte[] buf = new byte[nr * format];
//読み取るバッファを指定
//バックバッファを読み取る
//フロントバッファを読み取りたい GL_FRONT を指定
glReadBuffer( GL_BACK );
// ClientMemory への Pack ( 詰め込み )方法の指定
// Defalut ( GL_RGBA, GL_UNSIGNED_BYTE )は 密にセットする
//
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
// FrameBuffer image を使う Texture Object
glGenTextures( 1, &gId );
glBindTexture( GL_TEXTURE_2D, gId );
}
App::draw() {
// Scene を描画
drawScene();
// FrameBuffer から Pixel Data 取得
glReadPixels(
500, //読み取る領域の左下隅のx座標
500, //読み取る領域の左下隅のy座標
SW, //読み取る領域の幅
SH, //読み取る領域の高さ
GL_RGBA, //取得したい色情報の形式()
GL_UNSIGNED_BYTE, //読み取ったデータを保存する配列の型
gBuf //読み取ったデータを保存する配列
);
// Image の更新
glBindTexture( GL_TEXTURE_2D, gId );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, SW, SH, 0,
GL_RGBA, GL_UNSIGNED_BYTE, gBuf );
// 一度かいた絵を利用して何かする
draw();
}
■ glReadBuffer
SYNTAX
void glReadBuffer( GLenum target )
DESC
読み取るバッファを指定
glReadPixels() などに影響する
// 最初の 1 回目の描画は FrontBuffer には未初期化の画像があることがわかる
glReadPixels( GL_FRONT );
■ glPixelStorei
SYNTAX
void glPixelStorei(GLenum pname, GLint val)
DESC
set pixel storage modes
Main Memory と OpenGL 側の Memory でデータを転送するときに
Data がどのように配置( Alignment )されているか教える
1, 2, 4, 8 を指定できる
初期値は 4
この数が 大きいほど 効率的に Data を転送できる
DEFAULT
4
POINT
unpack とは Application が GL に Pixel Data を送り出す時の指定方法
"store" 命令 と考えれば OK
GL_UNPACK_ALIGNMENT
Specifies the alignment requirements
for the start of each pixel row in memory.
The allowable values are
1 (byte-alignment),
2 (rows aligned to even-numbered bytes),
4 (word-alignment),
8 (rows start on double-word boundaries).
The initial value is 4.
// ClientMemory から Read する方法を指定する
// だから glTexImage2D() にとってとても重要
// バッファの読み取り方を指定する
// Alignment を指定する
// glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
WARNING
// 1 Pixel 3 byte の raw format を利用するときは必須
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
void *img = readRawTexture( "./test.raw" );
■ glCopyPixels
SYNTAX
glCopyPixels(
int x, y, // 読み取り位置
unsigned int w, h, // 大きさ
int type // Copy する Buffer の指定 ( GL_COLOR | GL_DEPTH | GL_STENCIL )
);
DESC
指定した Buffer を RasterPos の位置に Copy
■ glCopyTexImage2D
SYNTAX
void glCopyTexImage2D(
int tgt,
int lvl,
int ifmt, // Copy すべき対象を指定する
// GL_LUMINANCE : 輝度値のみ
// TextureObject にも DepthBuffer がある
int x,y,w,h, // Copy する FrameBuffer の矩形
// FrameBuffer( ReadBuffer ) の 左下.w, h は, FrameBuffer の左下からの サイズ.
// 小さい値をセットすれば当然きれる ( 観測すみ. )
int border
);
DESC
FrameBuffer( BackBuffer )から 画像data を TextureObject に Copy する
MultiPath には必須の API
TIP
parameter は glTexImage2D() と同じ
pixel data の転送元が異なるだけ
ARG3 : ReadBuffer からどの値をとるよーという宣言.
-> BackBuffer は, RGB + DEPTH だったから別々にとることも可能.
-> Texture
も Buffer だから どんな内部構成( ifmt )にするかの指定が可能
WARNING
w, h は 2冪.(VER:2.0 )
画面全体を copy するならば, 2 冪でないとだめ ?
// Depth 値もとれる
glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16,
0, 0, vpW, vpH, 0 );
// Copy 先の Texture Object を選択
glBindTexture( GL_TEXTURE_2D, id );
if ( 0 ) {
glReadPixels(
500, //読み取る領域の左下隅のx座標
500, //読み取る領域の左下隅のy座標
SW, //読み取る領域の幅
SH, //読み取る領域の高さ
GL_RGBA, //取得したい色情報の形式()
GL_UNSIGNED_BYTE, //読み取ったデータを保存する配列の型
gBuf //読み取ったデータを保存する配列
);
// Image の更新
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, SW, SH, 0,
GL_RGBA, GL_UNSIGNED_BYTE, gBuf );
}
else {
// こちらの方が高速
glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
500, 500, SW, SH, 0 );
}
■ glCopyTexSubImage2D
SYNTAX
glCopyTexSubImage2D(
GLenum target,
GLint level,
GLint x, y, // Copy 先の Texture の位置
GLint x, y, // Copy 元の FrameBuffer の位置と大きさ
GLsizei w, h
)
WARNING
CubeMap には 裏側から利用するため Texture を反転する
■ スプライト(Sprite)
SAMPLE
スプライト
加算処理のスプライト
.
POINT
再利用性を高めるためにクラスにまとめること
#define W 1024
#define H 512
App::onDraw() {
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// 3Dシーンの描画
draw3D();
// デプステストを無効にする。
glDisable( GL_DEPTH_TEST );
// テクスチャの透明色の部分を透過させるためブレンディングを有効にする
glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 2Dを描画するための変換行列を設定する
// 画面サイズと同じ大きさの等角投影を設定しておくと、スプライト位置の指定が簡単になる。
// 奥行きは適当。
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, W, 0, H, 0, 1 );
// モデルビュー変換は不要
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// スプライトテクスチャを描画して FPS を描画
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, id );
}
■ スプライトを利用したGUIボタン
SAMPLE
GUIボタン
■ 頂点スキニング
SAMPLE
頂点スキニング
頂点スキニングを使ったバネモデル
App::onDraw() {
float m0[16], m1[16];
// 現在の骨の位置に移動する。
glLoadIdentity();
glTranslatef( 0, 50, 0 );
// 回転する。
glRotatef( m_timer, 0,0,1 );
// 骨の位置を基準(Pivot)にして回転するため、逆行列をかけて原点に移動する。
glTranslatef( 0, -50, 0 );
glGetFloatv( GL_MODELVIEW_MATRIX, mb0 );
// 2番目の骨も同じように計算
glLoadIdentity();
glTranslatef( 100, 50, 0 );
glRotatef( m_timer, 0,0,1 );
glTranslatef( -100, -50, 0 );
glGetFloatv( GL_MODELVIEW_MATRIX, mb1 );
{
CGparameter param;
// 頂点シェーダで変形させるため、骨の行列をシェーダに渡す。
float m[] = {
m0[0], m0[4], m0[ 8], m0[12],
m0[1], m0[5], m0[ 9], m0[13],
m0[2], m0[6], m0[10], m0[14],
m0[3], m0[7], m0[11], m0[15],
m1[0], m1[4], m1[ 8], m1[12],
m1[1], m1[5], m1[ 9], m1[13],
m1[2], m1[6], m1[10], m1[14],
m1[3], m1[7], m1[11], m1[15],
};
param = cgGetNamedParameter( shdV, "m" );
cgGLSetParameterArray4f( param, 0, 2*4, m );
// モデルビュー, プロジェクションを渡す。
glMatrixMode( GL_MODELVIEW_MATRIX );
glLoadIdentity();
convertCameraSpace();
param = cgGetNamedParameter( shdV, "mvp" );
cgGLSetStateMatrixParameter( param,
CG_GL_MODELVIEW_PROJECTION_MATRIX,
CG_GL_MATRIX_IDENTITY
);
}
// 描画する。( ウェイトの値は glNormal で指定しておく。 )
glBegin( GL_TRIANGLE_STRIP );
{
glColor4f(1,1,1,1);
for( int j=0; j< 2*7; j++ ){
// ウェイト値
float w0 = 1 - 1/7.0f*(j/2);
float w1 = 1 - w0;
glNormal3f( w0, w1, 1 );
float x = 2*(w1 - 0.5f) * 100;
float y = 100 * (j%2);
glVertex3f( x, y, 0 );
}
}
glEnd();
}
頂点シェーダで頂点を変形する
float4x4 mtx0 = float4x4( m[0], m[1], m[2], m[3] );
float4x4 mtx1 = float4x4( m[4], m[5], m[6], m[7] );
// ウェイト
float w0 = IN .nrm.x;
float w1 = IN .nrm.y;
// 変形
float4 p = w0 * mul( mtx0, IN .pos ) + w1 * mul( mtx1, IN .pos );
// モデルビュープロジェクション変換
OUT .pos = mul( mvp, pt );
■ CPUで変形する
CPUで計算するには頂点座標を指定する( glVertex() )前に変換をしておく。
float v[2*7*3];
for( int j=0; j< 2*7; j++ ){
// ウェイト値
float w0 = 1 - 1/7.0f*(j/2);
float w1 = 1 - w0;
float x = 2*(w1 - 0.5f) * 100;
float y = 100 * (j%2);
// 行列とベクトルの積
multiMatrix( mb0, v0, v0 );
multiMatrix( mb1, v1, v1 );
// ウェイトをかけてブレンドする
v[3*j+0] = w0*v0[0] + w1*v1[0];
v[3*j+1] = w0*v0[1] + w1*v1[1];
v[3*j+2] = 0;
}
glBegin( GL_TRIANGLE_STRIP );
glColor4f(1,1,1,1);
for( int j=0; j< 2*7; j++ ){
// 事前計算した頂点を指定する
glVertex3fv( v + 3*j );
}
glEnd();
行列とベクトルの積には glMultMatrix() を利用する。
void multiMatrix( const float *m, float *v, float *vout ) {
glMatrixMode( GL_MODELVIEW );
glLoadMatrixf( m );
float mt[16] = {
v[0], v[1], v[2], 1, // 第1列をベクトル扱いにする。
0, 0, 0, 0, // 残りの3列は仮
0, 0, 0, 0,
0, 0, 0, 0,
};
// 右からかける
glMultMatrixf( mt );
// 計算結果として行列同士の積の結果の第一列を取得する
glGetFloatv(GL_MODELVIEW_MATRIX, mt );
vout[0] = mt[0];
vout[1] = mt[1];
vout[2] = mt[2];
}
■ 頂点配列でウェイトを指定する
頂点の属性としてウェイトを指定するには glVertexAttribPointer() を利用して
汎用型の属性のひとつとして指定する。
App::onInit() {
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer" );
}
#define ATTR1 1
App::onDraw() {
struct Vertex {
float pos[3];
float col[3];
float weight[4];
};
Vertex v[2*NR];
{
for( int i=0; i< 2*NR; i++ ){
float w0 = 1 - 1.0f/(NR-1)*(i/2);
float w1 = 1 - w0;
float x = 2*(w1 - 0.5f) * 100;
float y = 100 * (i%2);
v[i].pos[0] = x;
v[i].pos[1] = y;
v[i].pos[2] = 0;
v[i].col[0] = 1;
v[i].col[1] = 0;
v[i].col[2] = 0;
v[i].weight[0] = w0;
}
}
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glEnableVertexAttribArray( ATTR1 );
glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), v );
glColorPointer ( 3, GL_FLOAT, sizeof(Vertex), v[0].col );
glVertexAttribPointer( ATTR1, 1, GL_FLOAT, false, sizeof(Vertex), v[0].weight );
// シェーダを有効にする。
cgGLEnableProfile( pfV );
glDrawArrays( GL_TRIANGLE_STRIP, 0, m_nr*2 );
}
頂点シェーダではアプリケーションで指定した頂点インデックスからウェイトを取得する。
struct app {
float4 pos : POSITION;
float4 weight : ATTR1;
};
float4x4 mtx0 = float4x4( m[0], m[1], m[2], m[3] );
float4x4 mtx1 = float4x4( m[4], m[5], m[6], m[7] );
// ウェイト
float w0 = IN .weight[0]
float w1 = 1 - w0;
// 変形
float4 p = w0 * mul( mtx0, IN .pos ) + w1 * mul( mtx1, IN .pos );
// モデルビュープロジェクション変換
OUT .pos = mul( mvp, p );
■ ステンシル(Stencil)
SAMPLE
ステンシル
DESC
ステンシルバッファを使うと輪郭の切り出しなどの、特殊エフェクトができる。
ステンシルバッファを設定する。
// 8 bit サイズで指定する。
pfd.cStencilBits = 8;
int fmt = ChoosePixelFormat( hDC, &pfd );
SetPixelFormat( hDC, fmt, &pfd );
最初にステンシル(型)をつくるため、輪郭にしたいオブジェクトを描画する。
次に 作成したステンシルとテストをして、描画する画面を切り抜く。
glEnable( GL_STENCIL_TEST );
glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE );
glStencilFunc( GL_ALWAYS, 0xff, 0xff );
// 型紙をつくるにはオブジェクトを描画する
drawPlane();
// ステンシルで切り抜く。( 0xff と同じ値のピクセルが描画される。 )
glStencilFunc( GL_EQUAL, 0xff, 0xff );
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
// 移りこみを表現するため、ワールド座標でY軸反転する。
glScalef( 1, -1, 1 );
glTranslatef( 0, 100, 0 );
drawCube();
// 通常のオブジェクトを描画する。
glDisable( GL_STENCIL_TEST );
drawCube();
■ glStencilOp
SYNTAX
void glStencilOp(
GLenum sfail, // ステンシルテストが失敗したピクセルに対する処理
GLenum dpfail, // ステンシルテストに合格したが デプステストに失敗したピクセルに対する処理
GLenum dppass // デプステストまで合格した( またはデプステスト無効 )ピクセルに対する処理。
)
DESC
ステンシルバッファへの処理を指定する。
3つの引数で、各テストに合格、不合格したピクセルごとに処理を設定できる。
set front and back stencil test actions
Stenciling,
like depth-buffering,
enables and disables drawing on a per-pixel basis.
You draw into the stencil planes using GL drawing primitives,
then render geometry and images,
using the stencil planes to mask out portions of the screen.
Stenciling is typically used in multipass rendering algorithms
to achieve special effects,
such as decals, outlining, and constructive solid geometry rendering.
次の8つの処理ができる。
GL_KEEP : 何もしない。
GL_REPLACE : glStencilFunc() の ref で指定した値で置き換える。
■ glStencilFunc
SYNTAX
void glStencilFunc(
GLenum func, // テスト演算子の指定
GLint ref, // テストで比較する値
GLuint mask // テスト実行前にステンシル値と ref の両方に bit mask する値
)
DESC
ステンシルテストの方法を設定する。
Specifies the test function. Eight symbolic constants are valid: GL_NEVER, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL, GL_EQUAL, GL_NOTEQUAL, and GL_ALWAYS. The initial value is GL_ALWAYS.
■ glClearStencil
SYNTAX
void glClearStencil( GLint s) // Specifies the index used when the stencil buffer is cleared. The initial value is 0.
DESC
ステンシルバッファのクリアする値を指定する。
glClearStencil specifies the index used by glClear to clear the stencil buffer. s is masked with m 2 - 1 ,
where m is the number of bits in the stencil buffer.
■ FrameBufferObject(FBO)
SAMPLE
フレームバッファオブジェクト
初期化時に FrameBufferObject を作成する
App::onInit() {
// フレームバッファオブジェクトを作成する
glGenFramebuffersEXT( 1, idFbo );
// fbo にアタッチするテクスチャオブジェクトを作成する
glGenTextures( 1, idTex );
glGenTextures( 1, idDepth );
// カラーバッファ
glBindTexture( GL_TEXTURE_2D, idTex );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
WARNING
// fbo に利用するテクスチャも 初期化の設定を正しく設定しないと
// 2パス目の描画でバインドしたときに正しく描画されない
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexEnvf ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
}
// 深度バッファを作成
glBindTexture( GL_TEXTURE_2D, idDepth );
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
// テクスチャをフレームバッファオブジェクトにバインド
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, idFbo );
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, idTex, 0 );
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, idDepth, 0 );
// デフォルトのレンダーターゲットに戻す
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
}
1パス目の描画で FrameBufferObject に描画する。
2パス目でその結果をバインドしたテクスチャを利用する。
App::onDraw() {
// バッファを変更
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, idfbo );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// ハコを描画
drawCube();
// 描画した結果を利用して 2 パス目を描画
// デフォルトのレンダーターゲットに戻し、ディスプレイに描画
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBindTexture( GL_TEXTURE_2D, idTex );
drawCube();
}
■ glBindFramebufferEXT
SYNTAX
void glBindFramebuffer(
GLenum target, // GL_DRAW_FRAMEBUFFER | GL_READ_FRAMEBUFFER | GL_FRAMEBUFFER
GLuint framebuffer // FrameBufferObject の ID
);
DESC
FrameBuffer で指定した fbo を target に設定する。
target は 3つのいずれかひとつ。
GL_DRAW_FRAMEBUFFER
描画の対象になる
GL_READ_FRAMEBUFFER
テクスチャへの読み込み処理の対象にする
// target に GL_FRAMEBUFFER を指定すると, 描画( draw ) と テクスチャへの読み込み( read )の両対応になる
// fbo オブジェクトは glGenFramebuffers() で作成する
POINT
// 0 を指定すると fbo オブジェクトの設定を解除する
// デフォルトに戻すときは必ずコールすること
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
■ glGenFramebuffers
SYNTAX
void glGenFramebuffers(
GLenum target, // GL_DRAW_FRAMEBUFFER | GL_READ_FRAMEBUFFER | GL_FRAMEBUFFER
GLuint framebuffer // FrameBufferObject の ID
);
■ glFramebufferTexture
void glFramebufferTexture2D( GLenum target,
GLenum attachment, // FrameBufferObject へバインドする場所
GLenum textarget, // アタッチするテクスチャオブジェクトのタイプ
GLuint texture, // Specifies the texture object to attach to the framebuffer attachment point named by attachment.
// アタッチ(設定)する TextureObject
GLint level // アタッチする TextureObject のミップマップレベルの指定
);
DESC
attach a level of a texture object
as a logical buffer to the currently bound framebuffer object
glFramebufferTexture, glFramebufferTexture1D,
glFramebufferTexture2D,
attach a selected mipmap level or image of a texture object
as one of the logical buffers of the framebuffer object currently bound to target.
target must be GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, or GL_FRAMEBUFFER.
GL_FRAMEBUFFER is equivalent to GL_DRAW_FRAMEBUFFER.
attachment
で FrameBufferObject のどこにテクスチャをバインドするか指定する
次のいずれか
GL_COLOR_ATTACHMENTi, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT GL_DEPTH_STENCIL_ATTACHMMENT.
i in GL_COLOR_ATTACHMENTi は以下の範囲
[ 0 : GL_MAX_COLOR_ATTACHMENTS - 1 ]
Attaching a level of a texture to GL_DEPTH_STENCIL_ATTACHMENT is equivalent to attaching that level to both the GL_DEPTH_ATTACHMENT
and the GL_STENCIL_ATTACHMENT attachment points simultaneously.
textarget
specifies what type of texture is named by texture,
and for cube map textures,
specifies the face that is to be attached.
If texture is not zero,
it must be the name of an existing texture with type textarget,
// cubemap でなければ
unless it is a cube map texture,
in which case textarget must be
GL_TEXTURE_CUBE_MAP_POSITIVE_X GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
texture
If texture is non-zero,
the specified level of the texture object named texture is attached
to the framebfufer attachment point named by attachment.
For glFramebufferTexture1D, glFramebufferTexture2D,
and glFramebufferTexture3D,
texture must be zero or the name of an existing texture with a target of textarget,
or texture must be the name of an existing cube-map texture
and textarget must be one of GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
■ 衝突判定
■ OBB.OBB
SAMPLE
OBB 同士の衝突判定
■ 空間分割
SAMPLE
空間分割
固定サイズの配列を用意して、点の場所ごとに仕分けをする。
各ボックス内にある点同士で判定をする。
総当りの数を減らす。
1000 個ならば 1000 * 1000 で 100 万回の判定をする。
4 分割して 250 個ならば
(* 250 250 4) 250.000
16 分割して 62 個ならば, 6 万回の判定をする。
(* 62 62 16) 61.500
1/4 ずつに減っていく
処理の順番
当たるなら動かない
1. 動く予定をしらべる
1. 当たった場合は動かない
3. あたる方向の成分をなくして 当たらない方向だけ移動する
( これをしないと 斜めに入力したときに, 完全に動かなくなる )
( 重力がある場合も同じ )
----
| | ---->
------------------------------
|
v
直方体どうしのあたり判定では , 斜めの表現はできない
■ k-dTree
均等分割は 一様に分布していないと性能が落ちる。
k-dTree は2分木で空間を分割する。
再帰的に空間を2つに分割していく。
■ sort
ソートする場合は 右から左に順番に検索していく。
--------- ----------
----------------
----
左側にマッチしたら リストに追加
右側にマッチしたら リストから削除( 領域から抜けたということ )
リスト内にあるものを判定する
動くもの同士の総当りを高速化する。
N*log(N) * N
■ 箱どうし
SAMPLE
箱どうしの衝突判定
矩形どうしが当たる場合は XY 軸ともに重なっている必要がある。
当たらない条件を考えると
A B
========= ===========
A.right < B.left
または
B A
========= ===========
B.right < A.left
A.right < B.left || B.right < A.left
これの偽だから
A.right >= B.left && B.right >= A.left
A
===========
============
B
bool testBoxBox( Box &a, Box &b ) )
{
if ( a.right >= b.left && b.right >= a.left ) {
if ( a.top >= b.bottom && b.top >= a.bottom ) {
return true;
}
}
return false;
}
当たった場合の処理として 動かないという対応がある。
この場合、移動するスピードが十分に小さければ、
隙間も目立たないので補正が不要という考えもある。
移動速度をあげると隙間が目立つ。
速度を上下するとわかる。
SAMPLE
衝突結果として動かない
この場合、斜めに移動をして衝突した場合も動けなくなる。
Y 方向の移動が 重力などの場合は 一切動けなくなる。
■ 位置の補正
SAMPLE
位置の補正
あたる直前の位置に補正するには、当たった後に重なった長さ分だけ元に戻す。
WARNING
XY 2軸同時に移動をする場合は、各軸独立して移動してから2回補正をする必要がある。
そうしないと横、縦方向にはじかれることになる。
■ 移動と速度
SAMPLE
移動と速度
最初に速度を変更して、それから位置を更新する。
■ 球球
2 点の中心の距離をもとめる
■ 三角形と線分
地面などに立つ場合につかう
POINT
あるフレームでの移動において使うことがポイント
こうすることで貫通が起きない
球と三角形では抜ける可能性がある
線分上のある点と 三角形上のある 点が一致するパラメータを求めればいい
a + t*b = c + u*X * v*Y
求めたい 未知数は 3 つ
3つの式が必要
無限平面でかんがえる
p = a + t*b となる p が平面にあれば
(p - c) は N と垂直である
N, c は定数としてもとまるので, パラメータ t だけが求まる
内積をつかうと
n.( c - p ) = 0
n . ( c - a + t*b ) = 0
n.c - n.a + t*n.b = 0 // 1次方程式
n.c - n.a
t = ------------- // t が求まる. 0:1 であるかチェックをする
n.b
n.b = 0 とならないようにチェックすること
幾何学的な意味は
N と b が平行なので当然、 交わらないということ
p = c + u*d + v*e
ベクタをスカラに変換するために 内積 を使える
// 1 次連立方程式
g.d = u*d.d + v*d.e
g.e = u*d.e + v*e.e
地道にとく
u を消すような 係数を 2 つの式にかける
v = ---------------
金利比較.COM