kentsu.dat

何かその時の興味でいろいろする人。最近はScala使ってる。アルゴリズムと自然言語処理、深層学習が大好き。

OpenGLをC言語で扱う①

OpenGLC言語で扱う

今回から画像処理以外にCGの部分も少しずつやっていこうと思ってこれからOpenGLC言語でぼちぼち扱っていこうと思う

インストールとかその他もろもろの環境構築は割愛

空ウィンドウを表示する

まずはとりあえずウィンドウだけを表示してみる
これ自体はすごく簡単なので先にサンプルコードを示す

#include<stdio.h>
#include<GL/glut.h>

void display(void);

int main(int argc,char *argv[]){
	
	//call window
	glutInit(&argc,argv);
	glutCreateWindow("sample window");//string literal or argv(string) 
	glutDisplayFunc(display);
	glutMainLoop();

	return 0;

}


void display(){
	//pass
}

まずここで登場する関数について説明

関数名 機能
void glutInit(int *argcp, char **argv) OpenGLの初期化を行いmain関数のパラメータをそのまま渡す
int glutCreateWindow(char *name) ウィンドウを開きタイトルバーの設定などを行う
void glutDisplayFunc(void (*func)(void)) ウィンドウ内に描画する関数。描画内容は関数のポインタとして渡した関数に依存
void glutMainLoop(void) イベントを待ち受ける関数


ざっくりまとめるなら

初期化→ウィンドウ作成→描画内容固めた関数渡して→ループさせる

みたいな感じで空ウィンドウは作成出来る


ただ一つ描画内容を固めるdisplayという独自関数がいつ呼ばれるのか
メインループがglutCreateWindowでウィンドウの生成が完了した段階で呼び出される
さらに言うなら再描画が必要なときも呼び出される

ウィンドウを塗りつぶしてみる

空ウィンドウのプログラムを実行するとわかるがウィンドウ下がそのまま表示されてしまっている
しかも位置は最初に呼び出した場所で固定

これではあまりに微妙な感じが出ているのでとりあえずウィンドウの背景を塗りつぶしてみる

#include<stdio.h>
#include<GL/glut.h>

void display(void);
void init(void);

int main(int argc,char *argv[]){
	
	//OpenGLの初期化
	glutInit(&argc,argv);

	//ディスプレイの表示モードの指定
	glutInitDisplayMode(GLUT_RGBA);

	glutCreateWindow("sample window ver 2");
	glutDisplayFunc(display);

	//描画で使うものの初期化
	init();

	glutMainLoop();

	return 0;
}


void display(){
	glClear(GL_COLOR_BUFFER_BIT);
	glFlush();
}

void init(){
	glClearColor(0.0,0.0,0.0,1.0);
}

ここで初めて出てきた関数の名前と機能

関数名 機能
void glutInitDisplayMode(unsigned int mode) ディスプレイの表示モードを指定する関数、GLUT_RGBAとGLUT_INDEXが使える
void glClearColor(GLclampf R, GLclampf G, GLclampf B, GLclampf A) ウィンドウを塗りつぶす色を指定する関数、α値は1で不透明となる
void glClear(GLbitfield mask) ウィンドウを塗りつぶす関数、塗りつぶすバッファを指定して使う
glFlush(void) まだ実行されてない命令をすべて実行させる関数。あまり呼び出しすぎると描画速度が低下する

ここでは色の指定などそういった初期化をinit(void)にまとめている
main関数やdisplay関数にまとめてもいいんけど、今後描画するものが多くなった時、複雑になった時などに
そういう記述が邪魔になるので分割する

2次元図形の描画と色の変更

今回はとりあえず今回は正方形を描画してみるが、ここで注意しなければならない点は座標は[-1,1]の範囲で固定されているという点で
これはx軸y軸の両方に言えることで、簡単に言えば単位円がちょうど書ける大きさというわけである

#include<stdio.h>
#include<GL/glut.h>


void display(void);
void init(void);

int main(int argc,char *argv[]){

	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_RGBA);
	glutCreateWindow("square 2d");
	glutDisplayFunc(display);
	init();
	glutMainLoop();

	return 0;

}

void display(void){
	glClear(GL_COLOR_BUFFER_BIT);

	//描画
	//描画タイプの指定
	glBegin(GL_LINE_LOOP);
	//線の色を指定
	glColor3d(0.0,0.0,1.0);
	//座標を設定
	glVertex2d(0.8,0.8);
	glVertex2d(-0.8,0.8);
	glVertex2d(-0.8,-0.8);
	glVertex2d(0.8,-0.8);
	//設定終了
	glEnd();


	//図形だけじゃ見づらいのでx,y軸も追加
	glBegin(GL_LINES);
	glColor3d(0.0,0.0,0.0);
	glVertex2d(-1,0);
	glVertex2d(1,0);
	glEnd();

	glBegin(GL_LINES);
	glColor3d(0.0,0.0,0.0);
	glVertex2d(0,-1);
	glVertex2d(0,1);
	glEnd();

	glFlush();
}

void init(void){
	//黒が背景は見づらいので白に変更
	glClearColor(1.0,1.0,1.0,1.0);
}

今回は表にするまでもなく手順が簡単なのでざっくり説明

glBeginで描画タイプ設定

glColor3dで線の色を設定できるので必要に応じて設定

glVertex2dで座標を設定x,yの順番

glEndで設定終了、描画

glFlushで強制的に描画できる
また描画タイプを変更することで図形内部を塗りつぶしたりもできる