OpenGL三角形を描画する


# しらべたこと
## シェーダー関係
### glCreateProgram
- 戻り値 (GLuint) プログラムオブジェクトのハンドル  
0が返ってきた場合はプログラムオブジェクトの作成に失敗している
- 引数 なし

空のプログラムオブジェクトを作成し、0以外の値を返す

- プログラムオブジェクトとは  
シェーダーオブジェクトをアタッチ可能なオブジェクト
※シェーダーオブジェクトについては後述する

### glCreateShader
- 戻り値 (GLuint) シェーダーオブジェクトのハンドル
- 引数 (GLenum shaderType) シェーダーの種類  

空のプログラムオブジェクトを作成し、0以外の値を返す
シェーダーの種類はいくつか存在して
コンピュートシェーダ、頂点シェーダー、ピクセルシェーダー、テッセレーションシェーダ、ジオメトリシェーダなどを指定する(GL_***_SHADERがそれらに該当する)
※コンピュートシェーダーはGLバージョン4.3以上で使用が可能

- シェーダーオブジェクトとは  
シェーダーコードの維持(?)に使用されるオブジェクト
自分は、自前のシェーダーをデータとして保持するためのオブジェクトという解釈をしている

### glShaderSource
- 戻り値 なし
- 引数 (GLenum shaderType, GLsizei count, const GLchar** string, const GLint* length)
    - shaderType シェーダーオブジェクトのハンドルを指定
    - count 引数stringとlengthで配列を指定してる場合、配列の要素数を指定
    - **string シェーダーのソースコード配列を指定
    - *length 引数string配列の各種文字列の長さを指定  
    `引数length` にNULLが指定されていた場合、配列の各文字列はNULL終端とみなされる  
    `引数length` にNULL以外が指定されていた場合、 `引数string配列` の各要素に対応する文字列の長さを指定する  

- `引数shader` で指定したシェーダーオブジェクトは `引数string配列` で指定したソースコードと関連付けられる
- シェーダーオブジェクトに元々関連付けされていた場合、それらのシェーダーは上書きされる
- この関数を使用した時点ではコードの解析やスキャンをおこなうわけではなく、単純にコードをシェーダーオブジェクトにコピーしているだけ

ここの翻訳ちょっと大変だった

### glCompileShader
- 戻り値 なし
- 引数 (GLuint shader) シェーダーオブジェクトのハンドル

指定したシェーダーオブジェクトに保存されているソースコードをコンパイルする
コンパイルステータスは、シェーダーオブジェクトの状態の一部として保存される
ステータスは `glGetShaderiv` 関数の引数に`GL_COMPILE_STATUS`を渡すことで取得することができる
さらに、エラーが存在する場合`glGetProgramiv`にGL_INFO_LOG_LENGTHを指定することで、ログの文字列の長さを取得することができる
`glGetShaderInfoLog` 関数を使用することによってコンパイルに関する情報を取得することが可能となっている

### glAttachShader
- 戻り値 なし
- 引数 (GLuint program, GLuint shader)  
    - program シェーダーオブジェクトがアタッチされているプログラムオブジェクトのハンドル
    - shader アタッチするシェーダーオブジェクトのハンドル

プログラムオブジェクトとシェーダーオブジェクトとを関連付ける
複数アタッチ可
`glDetachShader`を使って指定したシェーダーオブジェクトをデタッチする

### glLinkProgram
- 戻り地 なし
- 引数 (GLuint program) 
  - program リンクしたいプログラムオブジェクトのハンドル

シェーダーをリンクする
ステータスは `glGetProgramiv` 関数の引数に`GL_LINK_STATUS`を渡すことで取得することができる
さらに、エラーが存在する場合`glGetProgramiv`にGL_INFO_LOG_LENGTHを指定することで、ログの文字列の長さを取得することができる
そして、`glGetProgramInfoLog` 関数を使用することによってリンクに関する情報を取得することが可能となっている
ここでエラーが返ってきたら、その都度調査という感じで
正直翻訳してて心が折れそうだったので途中でやめた

---------------------------------------------------------------------------
## バッファの作成
### glGenVertexArrays
- 戻り値 なし
- 引数 (GLsizei n, GLuint* arrays) 
  - n 生成する頂点オブジェクトの数を指定する
  - arrays ↑で指定した数だけ頂点オブジェクトのID格納する配列を指定する

arraysに返ってくるIDは連続したものである保証はない
glDeleteVertexArraysで削除しない限り、その後glGenVertexArraysを使用しても同じIDが重複することはない

### glGenBuffers
- 戻り値 なし
- 引数(GLsizei n, GLuint* buffers)
  - n 生成するバッファオブジェクトの数を指定する
  - buffers ↑で指定した数だけバッファオブジェクトのID格納する配列を指定する

arraysに返ってくるIDは連続したものである保証はない
glDeleteVertexArraysで削除しない限り、その後glDeleteBuffersを使用しても同じIDが重複することはない

### glBindVertexArray
- 戻り値 なし
- 引数(GLuint array)
  - array 紐付けしたい頂点配列の名前(glGenVertexArraysで生成したIDを指定する)

生成したIDを指定することによってその後の処理と関連付けをする([glBufferData](#glbindbuffer)等)
0を指定すると以前のバインディングを切り離す

### glBindBuffer
- 戻り値 なし
- 引数(GLenum target, GLuint buffer)
  - target バッファオブジェクトがバインドされるターゲットを指定する  
ターゲットについては、色々あるのでリファレンスを要参照
  - buffer バッファのIDを指定する

生成したIDを指定することによってその後の処理と関連付けをする([glBufferData](#glbindbuffer)等)
0を指定すると以前のバインディングを切り離す

### glBufferData
- 戻り値 なし
- 引数(GLenum target, GLsizeiptr size, const void* data, GLenum usage)
  - target バッファオブジェクトがバインドされるターゲットを指定する  
ターゲットについては、glBindBuffer同様色々あるのでリファレンスを要参照
  - size データのサイズをバイト単位で指定する
  - data セットしたいデータのポインタを指定。NULL指定も可
  - usage データの使用方法

実際のデータをGPUへ送信する関数
引数`data`にNULLを指定した場合、データはコピーされない

### glEnableVertexAttribArray
- 戻り値 なし
- 引数(GLuint index)
  - index 有効にする頂点属性のインデックスを指定する

[glBufferData](#glbufferdata)で送ったデータの内訳を明確にするために使う
後々GLSLのコード内で`location`という形で`index`の値が使われる

### glVertexAttribPointer
- 戻り値 なし
- 引数(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer)
  - index 変更する頂点属性のインデックスを指定する
  - size 頂点属性の数を指定する 1～4の値、もしくは`GL_BGRA`でなければならない
  - type データ型を指定する (初期値 `GL_FLOAT`)
  - normalized 正規化をする場合は`GL_TRUE` そうでない場合は`GL_FALSE` 
  - stride 連続した頂点属性のバイトオフセットを指定する
  - pointer データ配列の先頭アドレスから数えたバイトオフセットを指定する

データ配列の頂点属性の内容を指定する関数
`normalized`については、型が符号付きの場合は-1～1, 符号なしの場合は0～1の値に正規化される
`stride`で値を指定することによって、座標3byte, 法線3byte, uv2byte が連続してる配列データ等1配列に複数の情報が格納されている場合を考慮できるようになる

#### 頂点オブジェクトとバッファオブジェクト
頂点オブジェクトがバッファオブジェクトを管理しているイメージ
各頂点の座標や法線等のデータをバッファオブジェクトとして管理し、それらをまとめて管理しているのが頂点オブジェクト的な

---------------------------------------------------------------------------
## 描画関係
### glGetUniformLocation
- 戻り値 (GLint) uniformのlocationID
- 引数(GLuint program, const GLchar* name)
  - program プログラムオブジェクトのIDを指定する
  - name シェーダー側で使用されているuniform変数の名前

uniform変数の位置を取得する
後々、glUniform関数で値を更新するのに使用したりする
uniformが配列の場合は`name`に添え字を加えることで、配列の値を個別に取得することもできる(？)
↑ためしてないので不確定な情報

### glUniform○○
- 戻り値 なし
- 引数 (GLint location, GLsizei count, GLboolean transpose <= あったりなかったりする, const GLfloat* value)
  -  location uniform変数のlocationIDを指定する
  -  count 設定する値の数 配列の場合は1より大きい値を指定する必要がある
  -  transpose 行列のuniformを設定するばあいに指定できる引数。 転地するかどうかをtrue falseで指定する
  -  v0,v1,v2,v3 それぞれ対応する値を引数に設定する
  -  value ベクターか行列の場合、配列の先頭アドレスを指定する

uniform変数の値を更新するのに使用する関数
公式ドキュメントには色々書いてあるけど、関数の命名規則とそれぞれの関数が何をしているかとかが書いてある気がする

### glDrawArrays
- 戻り値 なし
- 引数 (GLenum mode, GLint first, GLsizei count)
  - mode 描画するポリゴンの形状を指定する 三角形・四角形・ライン・点などが指定できる
  - first 配列の先頭アドレスを指定する
  - count 配列の要素数を指定する

ポリゴンを描画する関数
引数`first`については、バッファをGPU側に送っている場合は0を指定する

### glDrawElements
#### glDrawArraysとの違い
端的に言ってしまうと、共通インデックスの概念を利用できる
インデックスに対して、座標や法線の情報をテーブルとして紐付けできるので、無駄のないデータ構造を構築することができるのだと思う

↓画像付きで解説されていてとてもわかりやすかった
とても参考になったサイト: [床井研究室 - 第8回 指標を用いた図形の描画](http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20090909)


---------------------------------------------------------------------------
##  ソースコード

```cpp:main.cpp

// 初期化処理
GLuint programObject, vao, vbo;
void Init(){
  // プログラムオブジェクトを作成
  programObject = glCreateProgram();

  // 頂点シェーダーコンパイルまで
  const GLuint vsShaderObject = glCreateShader();
  const GLchar* vsCode = "頂点しぇーだーのコード";
  glShaderSource(vsShaderObject, 1, &vsCode, NULL);
  glCompileShader(vsShaderObject);
  glAttachShader(programObject, vsShaderObject);

  // フラグメントシェーダーコンパイルまで
  const GLuint fsShaderObject = glCreateShader();
  const GLchar* fsCode = "フラグメントしぇーだーのコード";
  glShaderSource(fsShaderObject, 1, &fsCode, NULL);
  glCompileShader(fsShaderObject);
  glAttachShader(programObject, fsShaderObject);

  // リンク
  glLinkProgram(programObject);

  /************************************************************/

  //頂点データの作成
  glGenVertexArrays(1, &vao);
  glGenBuffers(1, &vbo);

  glBindVertexArray(vao);
  glBindVertexArray(GL_ARRAY_BUFFER, vbo);
  
  static const float positionArray[] = {
    0.0f, 1.0f, 0.0f,
   -1.0f,-1.0f, 0.0f,
    1.0f,-1.0f, 0.0f,
  };
  // 頂点データ作成
  glBufferData(GL_ARRAY_BUFFER, sizeof(positionArray), positionArray, GL_STATIC_DRAW);

  // location = 0 の属性設定
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindArray(0);
}

// ------------------------------------------------

// ループ

void Loop(){
  // 使用するプログラムを指定
  glUserProgram(programObject);
  glBindVertexArray(vao);

  // モデル行列の値を指定
  const GLint modelLocation = glGetUniformLocation(programObject, "aModel");
  glUniformMatrix4fv(modelLocation, 1, GL_FALSE, modelMat/*モデル行列のポインタ*/);
  // ビュー行列の値を指定
  const GLint viewLocation = glGetUniformLocation(programObject, "aView");
  glUniformMatrix4fv(viewLocation, 1, GL_FALSE, viewMat/*ビュー行列のポインタ*/);
  // パースペクティブ行列の値を指定
  const GLint perspectiveLocation = glGetUniformLocation(programObject, "aProjection");
  glUniformMatrix4fv(perspectiveLocation, 1, GL_FALSE, perspectiveMatrix/*射影行列のポインタ*/);

  glDrawArray(GL_TRIANGLES, 0, 9);

  glBindVertexArray(0);
  glUserProgram(0);
}

// ------------------------------------------------

int main(){
  Init();

  // ～なんかいい感じにループする処理 start～
  Loop();
  // ～なんかいい感じにループする処理 end～

  return 0;
}

```

```glsl:draw.vert
#version 430 core

layout (location = 0) in vec3 aPosition;

uniform mat4 aModel;
uniform mat4 aView;
uniform mat4 aProjection;

out vec3 FragPosition;

void main(){
  FragPosition = vec3(aModel * vec4(aPosition, 1.0));
  gl_Position = aProjection * aView * vec4(FragPosition, 1.0);
}";
```

```glsl:draw.frag
#version 430 core;

out vec4 FragColor;

void main(){
  FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
```

## 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329073/10d8a70f-239f-ae3f-d4be-81482c23a8df.png)


# 参考ページ
OpenGL4.5 Reference Pages
https://www.khronos.org/registry/OpenGL-Refpages/gl4/
床井研究室 - 第8回 指標を用いた図形の描画
http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20090909