OpenGL/直方体の作り方


<table>
  <tr>
    <td><a href="https://qiita.com/yu-ka/items/05b39b5c2a77654c92e5#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a><br>
        <a href="https://qiita.com/yu-ka/items/05b39b5c2a77654c92e5#opengl%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E7%9B%B4%E6%96%B9%E4%BD%93%E3%81%AE%E4%BD%9C%E6%88%90%E6%96%B9%E6%B3%95">OpenGLにおける直方体の作成方法</a><br>
        <a href="https://qiita.com/yu-ka/items/05b39b5c2a77654c92e5#%E7%9B%B4%E6%96%B9%E4%BD%93%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B%E9%96%A2%E6%95%B0boxh%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A8%E4%BB%95%E7%B5%84%E3%81%BF">直方体を作成する関数box.hのコードと仕組み</a><br>
        <a href="https://qiita.com/yu-ka/items/05b39b5c2a77654c92e5#boxh%E3%81%AE%E5%B0%8E%E5%85%A5%E3%81%A8%E4%BD%BF%E3%81%84%E6%96%B9">box.hの導入と使い方</a><br>
        <a href="https://qiita.com/yu-ka/items/05b39b5c2a77654c92e5#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a><br>
  </tr>
</table>

## はじめに
　大学の講義で、OpenGLを使ってロボットを作成する課題があり、直方体で胴体等を作ろうと思ったのですが、OpenGLには直方体の描画を行うプリミティブ関数がありませんでした。そこで、講義で公開されていた円柱を作成するサンプルプログラムを参考にして、直方体を作るインクルードファイルを作成しました。
　本投稿では、そもそもOpenGLで直方体はどのように作成できるのかや、私が今回作成したbox.hの説明、そしてその使い方について説明していきたいと思います。

## OpenGLにおける直方体の作成方法
　直方体の作成方法は、主に2つあります。

　1. 立方体を作成し、一定方向に拡大する!
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/72c01744-c858-7c01-a281-d4ee9b24c6fb.png" width="100%"><br>
　直方体ではなく立方体を作成する関数なら存在します。glutSolidCubeを利用して、まず立方体を描画します。次に、glScalefを使って座標軸ごと拡大します。この際、glScalefは法線ベクトルまで拡大縮小してしまい単位ベクトルではなくなってしまうため、glEnable( GL_NORMALIZE )で描画の時に単位ベクトルになるようにしておく必要があります。この方法のメリットは、コードが簡単で短いことです。
　前述のように作成した直方体と、そのコードを以下に示します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/eec109ca-c3a0-9019-bc0c-015925c6fb7a.png" width="50%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/2e19fb26-1b26-3312-b068-9afae55eb4c0.png" width="50%">
```C
glEnable( GL_NORMALIZE );
glScalef(1.0,1.5,1.0);
glutSolidCube(2.0);
glDisable( GL_NORMALIZE );
```
　

　2. 1つずつ面を作成する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/d8a6a0ea-5050-25b2-da66-f7c9bc3921e7.png" width="16.6%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/f0ce1b9e-d50b-2b7a-9b86-152011dd62f3.png" width="16.7%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/3c44fb67-2448-ce72-4ebe-221c30fdd66f.png" width="16.7%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/3d40912c-e13a-2754-e542-2335753c02ca.png" width="16.7%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/b1b74b16-b6b6-eeca-fe63-7ed2d9b0725f.png" width="16.7%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/17f50c1a-880b-bfd4-0735-e2648fb04257.png" width="16.6%">
　直方体を構成する6面の長方形を1つずつglBegin( GL_QUADS );で作成して直方体を実装します。これには6面それぞれの四角形の頂点4つの座標を記述する必要があります。また、それぞれの面に対してglNormal3fでしっかりと法線ベクトルを設定する必要があります。この方法のメリットは、座標系を変化させることなく直方体を作成できることです。
　作成した直方体と、そのコードを以下に示します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/16237ead-7598-4e67-596c-5e1493cf5a1b.png" width="100%">
```C
glEnable( GL_NORMALIZE );

glTranslatef(0,-1.5,0); //1.と結果が同じになるように座標系を移動
glBegin( GL_QUADS ); //四角形を作成１
glNormal3f( 0.0, 1.0, 0.0 ); //法線ベクトルを設定
  glVertex3f( -1.0, 3.0, -1.0 );//外側の面が表になるように反時計回りに４点を指定
  glVertex3f( 1.0, 3.0, -1.0 );	
  glVertex3f( 1.0, 3.0, 1.0 );
  glVertex3f( -1.0, 3.0, 1.0 );
glEnd();

glBegin( GL_QUADS ); //四角形を作成２
glNormal3f( 0.0, -1.0, 0.0 );
  glVertex3f( -1.0, 0, -1.0 );
  glVertex3f( -1.0, 0, 1.0 );
  glVertex3f( 1.0, 0, 1.0 );
  glVertex3f( 1.0, 0, -1.0 );
glEnd();

glBegin( GL_QUADS ); //四角形を作成３
glNormal3f( -1.0, 0.0, 0.0 );
  glVertex3f( -1.0, 3.0, -1.0 );
  glVertex3f( -1.0, 3.0, 1.0 );
  glVertex3f( -1.0, 0, 1.0 );
  glVertex3f( -1.0, 0, -1.0 );
glEnd();

glBegin( GL_QUADS ); //四角形を作成４
glNormal3f( 0.0, 0.0, 1.0 );
  glVertex3f( -1.0, 3.0, 1.0 );
  glVertex3f( 1.0, 3.0, 1.0 );
  glVertex3f( 1.0, 0, 1.0 );
  glVertex3f( -1.0, 0, 1.0 );
glEnd();

glBegin( GL_QUADS ); //四角形を作成５
glNormal3f( 1.0, 0.0, 0.0 );
  glVertex3f( 1.0, 3.0, 1.0 );
  glVertex3f( 1.0, 3.0, -1.0 );
  glVertex3f( 1.0, 0, -1.0 );
  glVertex3f( 1.0, 0, 1.0 );
glEnd();

glBegin( GL_QUADS ); //四角形を作成６
glNormal3f( 0.0, 0.0, -1.0 );
  glVertex3f( 1.0, 3.0, -1.0 );
  glVertex3f( 1.0, 0, -1.0 );
  glVertex3f( -1.0, 0, -1.0 );
  glVertex3f( -1.0, 3.0, -1.0 );
glEnd();

glDisable( GL_NORMALIZE );
 ```


## 直方体を作成する関数box.hのコードと仕組み
　ここまでで直方体の作成方法を2つほど紹介してきましたが、2つ目の方法を応用することでどちらのメリットも満たすことができます。それが、インクルードファイルです。頂点座標の記述には大量の行数が必要になりましたが、実際に使われている相対的な座標はすべて『直方体の幅、奥行き、高さ』の情報があれば計算して求めることができます。この3つの情報を引数とする関数を作成して、描写部分のコードをインクルードファイルにまとめておいて、関数を引き出せばすべて自動で描写してくれるという方法を考え実装しました。インクルードファイル名をbox.h、プログラム内で呼び出す関数名をmakeboxとしています。
　box.hの内容を以下に示します。
```C:box.h
void makebox( float width, float depth, float height )
{	
	glEnable( GL_NORMALIZE );

	glBegin( GL_QUADS ); //四角形を作成
	glNormal3f( 0.0, 1.0, 0.0 ); //法線ベクトルを設定
		glVertex3f( -width/2, height, -depth/2 );//外側の面が表になるように反時計回りに４点を指定
		glVertex3f( width/2, height, -depth/2 );	
		glVertex3f( width/2, height, depth/2 );
		glVertex3f( -width/2, height, depth/2 );
	glEnd();
	glBegin( GL_QUADS );
	glNormal3f( 0.0, -1.0, 0.0 );
		glVertex3f( -width/2, 0, -depth/2 );
		glVertex3f( -width/2, 0, depth/2 );
		glVertex3f( width/2, 0, depth/2 );
		glVertex3f( width/2, 0, -depth/2 );
	glEnd();
	glBegin( GL_QUADS );
	glNormal3f( -1.0, 0.0, 0.0 );
		glVertex3f( -width/2, height, -depth/2 );
		glVertex3f( -width/2, height, depth/2 );
		glVertex3f( -width/2, 0, depth/2 );
		glVertex3f( -width/2, 0, -depth/2 );
	glEnd();
	glBegin( GL_QUADS );
	glNormal3f( 0.0, 0.0, 1.0 );
		glVertex3f( -width/2, height, depth/2 );
		glVertex3f( width/2, height, depth/2 );
		glVertex3f( width/2, 0, depth/2 );
		glVertex3f( -width/2, 0, depth/2 );
	glEnd();
	glBegin( GL_QUADS );
	glNormal3f( 1.0, 0.0, 0.0 );
		glVertex3f( width/2, height, depth/2 );
		glVertex3f( width/2, height, -depth/2 );
		glVertex3f( width/2, 0, -depth/2 );
		glVertex3f( width/2, 0, depth/2 );
	glEnd();
	glBegin( GL_QUADS );
	glNormal3f( 0.0, 0.0, -1.0 );
		glVertex3f( width/2, height, -depth/2 );
		glVertex3f( width/2, 0, -depth/2 );
		glVertex3f( -width/2, 0, -depth/2 );
		glVertex3f( -width/2, height, -depth/2 );
	glEnd();
	
	glDisable( GL_NORMALIZE );
}
```
 引数の幅(width)と奥行き(depth)をもとに原点を中心として底面が作成され、y軸方向に高さ(height)だけ伸びるような直方体となっています。以下が座標と作成された直方体のイメージ図です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984887/83c73810-002c-e226-f885-81e7f5c5637d.jpeg" width="100%">

## box.hの導入と使い方
　まず、box.hを作成します。実際は私がbox.hを添付してそれを保存してもらうだけで使えるのですが、ファイル添付方法がわからなかったため作成方法を紹介します。.hファイルを作成し、ファイル名をbox.hとします。(違う名前にする場合は、以降のbox.hをそのファイル名に変更してください。)作成したファイルに前述したbox.hのコードをすべて貼り付けます。
 次に、使いたいプログラム内で作成したbox.hファイルをインクルードします。この際、<GL/glut.h>のように< >で囲うのではなく、"box.h"のように" "で囲うようにしてください。
　最後に、使いたい部分でmakebox(width,depth,height)を呼び出します。引数にはx軸方向の幅、z軸方向の奥行き、y軸方向の高さを指定してください。実行すれば、直方体が作成できているはずです。
```C
#include <box.h> //　✕
#include "box.h" //　〇
```

## おわりに
　OpenGLにおける直方体の作成方法について記させていただきました。講義の範囲を応用しただけなので、理解の乏しい点や間違っている点などありましたら申し訳ございません。Qiita自体が初投稿ということもあり、至らない点もあったかと思いますが、記述方法などを学べてとても良かったです。
　指摘や質問などがございましたら、ぜひぜひコメントしていただけると嬉しいです。読んでいただき、ありがとうございました。