射影変換


透視法射影

ここでは、射影変換を解説します
これまでの頂点は2次元のものでしたが、これでようやく Z 座標を自由に使えるようになります

射影変換の目的は視体積の定義です
どの程度の範囲で、どこまで見ることができるのかという問題を解決します
これは、カメラのレンズを選択する作業に似ています

射影行列を選択するために、最初に glMatrixMode() で GL_PROJECTION を指定します
そして、必要であれば単位行列をセットしてから、変換を行うと良いでしょう

射影には、代表して2つの方法が存在します
そのひとつが透視法射影と呼ばれるもので
人間の感覚に極めて近い遠近法による描画を行います

透視法射影は、近くにあるものほど大きく、遠くにあるものほど小さく見せます
つまり、視野が視点を頭としたピラミッド型のように構成されているのです
人間の視点で世界を見ているような、リアリティが求められる空間に使うことができます

透視法射影を操作するための行列を作る式があるわけですが
非常に難解なので、この場で原理の説明を行うのは省略することにします
(難解なのは数学的にではなく、テキストで分数などの式を表現することが…という意味で)
透視射影行列の設定は glFrustum() 関数が行ってくれるでしょう
void glFrustum(
	GLdouble left , GLdouble right ,
	GLdouble bottom , GLdouble top ,
	GLdouble znear , GLdouble zfar     
);
left と right には、左と右の垂直座標を
bottom と top には、上と下の水平座標をそれぞれ指定します
これらは、オブジェクトの X 座標と Y 座標の見え方に影響します

znear は視点から最も近く、zfar は最も遠いクリップ面の距離を指定します
これらは、オブジェクトの Z 座標の見え方に影響します
もちろん znear 〜 zfar の間以外のオブジェクトは見ることができません
znear と zfar は、必ず正の値でなければなりません

この関数を実行すると、要求した数値を元に射影変換が行われます
これまでは、幅と高さ共に -1 〜 1 までの値しか指定することができませんでしたが
射影変換を行えば、よりワイドに空間を指定することが可能です

Z 座標においては、値が高ければ高いほど近く、小さければ小さいほど遠くなります
デフォルトで視点は負の Z 座標に向いているため、より近いものは -1 になり
それよりも小さい値になればなるほど、遠くに見えます
もちろん、射影行列で指定されている距離以上の距離にあるオブジェクトは見えません

3次元プログラミングの座標系について、大きく2つの考え方があります
それは、遠くの物体ほど値が大きい左手座標系
手前の物体ほど値が大きい右手座標系です
上記の解説からわかるように OpenGL は右手座標系であることに注意してください

3次元グラフィックスで有名な Microsoft DirectX では左手座標系が使われています
左手座標系への移植を考慮するならば、視点を負の Z 座標に向かせればよいでしょう
そうすれば、マイナス符号の絶対値が増えれば増えるほど遠くになるため
符号を反転させるだけで(絶対値を変更せずに)左手座標系に変換することができます
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

const GLfloat vertex[] = {
	3.1 , 1.9 , -3 , 4.5 , 0.1 , -3 , 6 , 1.9 , -3 ,
	0.1 , 1.9 , -2 , 1.5 , 0.1 , -2 , 3 , 1.9 , -2 
};

void disp( void ) {
	glClear(GL_COLOR_BUFFER_BIT);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3 , GL_FLOAT , 0 , vertex);

	glBegin(GL_TRIANGLES); {
		int i;
		glColor3f(0 , 0 , 1);
		for(i = 0 ; i < 3 ; i++) glArrayElement(i);
		glColor3f(1 , 0 , 0);
		for(i = 3 ; i < 6 ; i++) glArrayElement(i);
	} glEnd();

	glFlush();
}

int main(int argc , char ** argv) {
	glutInit(&argc , argv);
	glutInitWindowPosition(100 , 50);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);

	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(0 , 4 , 2 , 0 , 2 , 10);

	glutMainLoop();
	return 0;
}


このプログラムは Z 座標 -2 の赤い三角形と
Z 座標 -3 の青い三角形をそれぞれレンダリングしています
見てわかるように、Z 座標値の低い青い三角形の方が遠くにあるように見えます
しかし、大きさは赤い三角形と同一であることに注目してください

透視射影行列の設定では、GLU も同じような関数を提供しています
この設定は glFrustum() でも十分ですが、GLU を使えば視野角で設定することができます
void gluPerspective(
	GLdouble fovy , GLdouble aspect ,
	GLdouble zNear , GLdouble zFar
);
fovy には Y 座標の、すなわち縦の視野角を指定します
aspect は視野角に対する比率で水平方向の視野角を決定します
zNear は近くのクリップ面、zFar は遠くのクリップ面までの距離を正の値で指定します
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

const GLfloat vertex[] = {
	-2 , 2 , -2 , 2 , 2 , -2 , 0 , -2 , -2 ,
};

void disp( void ) {
	glClear(GL_COLOR_BUFFER_BIT);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3 , GL_FLOAT , 0 , vertex);

	glDrawArrays(GL_POLYGON , 0 , 3);

	glFlush();
}

int main(int argc , char ** argv) {
	glutInit(&argc , argv);
	glutInitWindowPosition(100 , 50);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);

	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(160 , 0.75 , 1 , 10);

	glutMainLoop();
	return 0;
}
このプログラムは、視野角を使って透視射影行列を設定しています
透視法射影はそもそも人間の視覚に合わせているので、視野角での設定は直観的です
縦に 160 度、横に 120 度(4:3)の設定で、透視射影行列を設定します


正射影

透視法射影とは異なり、直方体の視体積を持つ射影を正射影と呼びます
正射影はピラミッド型の視体積にはならず、オブジェクトへの距離は描画に影響されません
つまり、遠近感が完全に麻痺した状態となります

これは、視覚に影響されず、常に正しいサイズを表示しなければならない時に使います
建築分野や、3次元オブジェクトの生成ツールなどには適しているでしょう
正射影の視体積を作成するには glOrtho() 関数を使います
void glOrtho(
	GLdouble left , GLdouble right ,
	GLdouble bottom , GLdouble top , 
	GLdouble near , GLdouble far      
);
引数はの意味は glFrustum() 関数と同じです
left と right には、クリップ面の左と右の垂直座標を
bottom と top には、上と下の水平座標をそれぞれ指定します
znear は視点から最も近く、zfar は最も遠いクリップ面の距離を指定します
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

const GLfloat vertex[] = {
	-0.9 , 0.9 , -4 , 0.9 , 0.9 , -2 , 0 , -0.9 , -2 ,
};

void disp( void ) {
	glClear(GL_COLOR_BUFFER_BIT);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3 , GL_FLOAT , 0 , vertex);

	glDrawArrays(GL_POLYGON , 0 , 3);

	glFlush();
}

int main(int argc , char ** argv) {
	glutInit(&argc , argv);
	glutInitWindowPosition(100 , 50);
	glutInitWindowSize(400 , 300);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);

	glutCreateWindow("Kitty on your lap");
	glutDisplayFunc(disp);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//glFrustum(-1 , 1 , -1 , 1 , 2 , 4);
	glOrtho(-1 , 1 , -1 , 1 , 2 , 4);

	glutMainLoop();
	return 0;
}
このプログラムでは glOrtho() を使って正射影を設定しています
レンダリングするポリゴンの頂点のひとつは、Z 座標が -4 に設定されています
透視法射影であれば、遠くにある頂点は遠近法によって小さく(中央より)になるはずですが
正射影では、視点からの距離が表示されるサイズに影響することはありません
コメント化されている glFrustum() と比較して見るとよくわかるでしょう



前のページへ戻る次のページへ