Archive for the ‘GLSL’ Category

[openGL, GLSL] openGL+GLSLのメモ #2 座標系

openGLとかGLSLやっていくうえで座標系がしっかり理解できてないとこの先理解できないので、
基礎的なことだけどちゃんとメモ。

座標系と変換の流れ

1. オブジェクト座標系(モデリング座標、ローカル座標)
オブジェクトの頂点や法線を表す座標系

  •     ▼ モデリング変換 : オブジェクト座標系をワールド座標系に変換
2. ワールド座標系(グローバル座標系)
空間中のオブジェクトの位置を示す座標系
  •     ▼ 視野変換(ビューイング変換) : ワールド座標系から視点座標系への変換
3. 視点座標系(眼点座標系)
空間を視点(カメラ)から見た空間へ変換した座標系。
  •     ▼ 射影変換 : 視点座標系からクリップ座標系への変換(オブジェクトの奥行き情報を保持する変換)
4. クリップ座標系
視点からの近平面と遠平面をクリッピングする座標系(たぶん)
  •     ▼ 遠近除算 : 遠近除算(正規化)処理をすることで、クリップ座標系から正規化デバイス座標系になる
5. 正規化デバイス座標系
クリップ座標系の座標 x, y, z 成分を w で割った座標系
  •     ▼ ビューポート変換 : 正規化デバイス座標系からウインドウ座標系への変換
6. ウインドウ座標系
表示画面に対応するピクセル単位の座標系




以下は実際のopenGLでの処理。流れは逆からになる。

ビューポート変換 5 → 6

正規化デバイス座標系からウインドウ座標系への変換
ウインドウに設定される実際の描画領域の設定
通常は表示ウインドウとビューポートのサイズを一致させて作る

void glViewport(GLint x , GLint y , GLsizei width , GLsizei height);



射影変換 3 → 4, 5

視点座標系からクリップ座標系に変換
投影の設定

glMatrixMode( GL_PROJECTION );
glLoadIdentity();


射影行列を使うことを宣言してから、変換を行う

・透視投影の場合
void gluPerspective(
	GLdouble fovy , GLdouble aspect ,
	GLdouble zNear , GLdouble zFar
);


aspectをビューポート変換で設定した width / height に設定することで
図形の長さの比が保たれるようになる

・正射影の場合
void glOrtho(
	GLdouble left , GLdouble right ,
	GLdouble bottom , GLdouble top , 
	GLdouble near , GLdouble far      
);



視野変換 2 → 3

ワールド座標系から視点座標系への変換
視点の設定と中心点の設定を行う

glMatrixMode( GL_MODELVIEW );
glLoadIdentity();


モデルビュー行列を使うことを宣言してから、変換を行う
void gluLookAt(GLdouble ex, GLdouble ey, GLdouble ez, 
			 GLdouble cx, GLdouble cy, GLdouble cz, 
			 GLdouble ux, GLdouble uy, GLdouble uz)
//例)
gluLookAt(3.0, 4.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);


ex, ey, ez : 視点(カメラ)の位置
cx, cy, cz : シーンの中心点
ux, uy, uz : 視体積の上方部分( 通常は 0.0, 1.0, 0.0 でおっけ)


モデリング変換 1 → 2

オブジェクト座標系をワールド座標系に変換
下記の関数を利用して変換することを「アフィン変換(モデリング変換)」という

glScale*(sx, sy, sz);
glRotate*(angle, ax, ay, az);
glTranslate*(tx, ty, tz);



オブジェクトの描画

実際に表示させるオブジェクトを描画する



参考

OpenGL+GLSLによる画像処理プログラミング―「OpenGL」と「シェーダ言語」で「レタッチ・ソフト」の仕組みを知る! (I・O BOOKS)
OpenGLの座標系





[openGL, GLSL] openGL+GLSLのメモ #1

GLSL hello world シェーダーむずいですね。とりあえずは基礎からしっかりとな感じで頑張ってます。



OpenGL+GLSLによる画像処理プログラミング
この本とwebを見ながらGLSLの勉強したメモ。 まだ全部読んでないしよくわかってないけど頭の整理もかねて。

シェーダの種類

  • ・バーテックスシェーダ(頂点シェーダ)
  • ・フラグメントシェーダ(ピクセルシェーダ)

バーテックスシェーダ(頂点シェーダ)
頂点データを扱う。
“位置・色・テクスチャ” とかの頂点の属性だけを参照・変更できる。
バーテックスシェーダで計算された頂点は、ジオメトリシェーダかフラグメントシェーダに渡される。

フラグメントシェーダ(ピクセルシェーダ)
各ピクセルに影響し、テクスチャを参照することで “バンプマッピング・フォグ・シャドウ・ブルーム” とかのエフェクトをレンダリング結果に適用させる。
wiki見るとこんな感じらしい。
とりあえずは、バーテックスシェーダで頂点情報を計算して、フラグメントシェーダでエフェクトかけて、openGLに渡す
っていう流れでいいんですかね。


次はシェーダープログラムのほうのメモ。

shader.frag と shader.vert

参考にさせてもらったサイトのバーテックスシェーダとフラグメントシェーダのソースをほんーーのちょっとイジったやつ。

//shader.vert

uniform int a; //openGLと値のやりとり用
void main(void)
{
    //transform 
	gl_Position = ftransform();
	
    // normal color 
	gl_FrontColor.rgb = 0.5 * gl_Normal.xyz + 0.5;
	gl_FrontColor.a = float(a) * 0.01;
}


//shader.frag

void main (void)
{
	gl_FragColor = gl_Color;    
}


shader.vertのgl_FrontColorの値がshader.fragのgl_Colorに渡されて、
shader.fragのgl_FragColorの値がopenGL側に適用される感じ?
次あたりここら辺はまた詳しく勉強してメモしたいです。


openGLとGLSLでのやりとり

GLSLを使うためのお決まり処理の流れは、参考サイトに書かれているものによると、

  • 1. バーテックスシェーダとフラグメントシェーダのシェーダオブジェクトを作成 – glCreateShader();
  • 2. 作成したそれぞれのシェーダオブジェクトに対してソースプログラムを読み込む – glShaderSource();
  • 3. 読み込んだソースプログラムをコンパイル – glCompileShader();
  • 4. プログラムオブジェクトを作成 – glCreateProgram();
  • 5. プログラムオブジェクトに対してシェーダオブジェクトを登録 – glAttachShader();
  • 6. シェーダプログラムをリンク – glLinkProgram();
  • 7. シェーダプログラムを適用 – glUseProgram()
こんな流れらしい。 コピペしましたすいません。
ここは覚えたほうがいいんだろうけど、クラス化しておけば触れずにすむところではあるので、ちょっとスルーで。
とりあえず、この本のサンプルのGLSLのお決まり処理クラスをちょっと変えて自分の使いやすいようにして使わせてもらいます。


以下はopenGLとGLSLで値のやりとりの部分のメモ。
//main.cpp

//シェーダーオブジェクト
GLuint shader;

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    
    glUseProgram(shader); //シェーダーの適用

    GLint a = 80; //openGLとシェーダーでやりとりする変数
    GLint _loc01 = glGetUniformLocation(shader, "a");
    glUniform1i(_loc01, a);
    
    GLfloat vertex[][2]=
    {
        {10,10},
        {100,10},
        {10,100},
        {100,100}
    };
    glVertexPointer(2, GL_FLOAT, 0, vertex);    
    glEnableClientState(GL_VERTEX_ARRAY);    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    glUseProgram(0);  //シェーダーの適用の終了
    
    glFlush();
}


大事なところだけで抜粋。initとかの関数は無視。

//main.cpp

glUseProgram(shader);
/**
 *この部分で描画処理
 */
glUseProgram(0);


ここの描画処理の中に書かれたものにシェーダーが適用される。
上のソースでいうとglDrawArraysで書いた四角形にシェーダーが適用されている。

//main.cpp

GLint a = 80;
GLint _loc01 = glGetUniformLocation(shader, "a"); //値を渡すための準備
glUniform1i(_loc01, a); //値を渡す


openGLとシェーダーの値のやりとりの部分。
glGetUniformLocation : シェーダプログラムに値を渡すため、変数のインデックスを取得する。
glUniform1i : openGLからシェーダプログラムに渡す。
こんな関数を使えばいいらしい。
あらかじめシェーダプログラム側に変数を用意しておいて、それのインデックスを取得、それを使って値をシェーダプログラムに渡す流れ。

なんとなく、openGLとGLSL使うときの基礎的なメモはこんな感じで、ちょっとだけ苦手意識は消えた気が。気だけ。少しだけ。
これからは時間みてもうちょっとシェーダプログラムとかopenGLとGLSLで値のやりとりの部分とか、サクッとコピペで終わったGLSLのお決まり処理の部分とかやってきます!