[Work/Class/OpenGL/2_OpenGL2toX]

glmによる変換行列生成と頂点シェーダ

とりあえず頂点の色は固定で,頂点シェーダのみの処理

/* GenerateMatrixVertexShader_1.cpp */
#include <GL/glew.h>

#ifdef __APPLE__
#include <OpenGL/opengl.h>
#else
#include <Gl/gl.h>
#endif

#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <string>
#include <vector>
#include <iostream>
#include <fstream>

//プロトタイプ宣言
GLuint loadShaders(const char* vertex_file_path,
		     const char* fragment_file_path);

//あらかじめ描画する三角形の座標を「1次元配列」として持っておく
//x1, y1, z1, x2, y2, z2, x3, y3, z3
//という感じで並べる
//GLFloatは要するにfloatそのまま
static const GLfloat myFirstTriangleVertexArray[9] = 
  {-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0};

int main(void){
  //OpenGL ver.3.2 Core Profileを選択する
  //内容は理解する必要はない
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //OSX用の指定
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  GLFWwindow* glfwWindow;
  if(!glfwInit()){
    return -1;
  }

  int windowWidth = 640;
  int windowHeight = 480;
  glfwWindow = 
    glfwCreateWindow(windowWidth, windowHeight,
		     "GenerateMatrixVertexShader_1",
		     NULL, NULL);
  if(glfwWindow == NULL){
    glfwTerminate();
    return -1;
  }
  
  //作成したウィンドウをOpenGLの処理対象にするContext登録
  glfwMakeContextCurrent(glfwWindow);
  //GLEWを初期化する
  glewExperimental = true;
  if(glewInit() != GLEW_OK){
    fprintf(stderr, "Cannot initialize GLEW\n");
    return -1;
  }

  //配列バッファオブジェクトを生成し,現在のContextに登録する
  GLuint vertexArrayID;
  glGenVertexArrays(1, &vertexArrayID);
  glBindVertexArray(vertexArrayID);

  //シェーダプログラムを使えるようにする
  GLuint programID = 
    loadShaders("GenerateMatrixVertexShader_1_VertexShader.glsl",
		"GenerateMatrixVertexShader_1_FragmentShader.glsl");
  

  //-------------------ここまでは,この順番で行う必要がある--------------------------

  //頂点バッファの初期化 
  //GLfloatの配列から,MainLoop内で描画を実行できる頂点バッファの形を作る作業である
  //モデルごとに GLuint型の変数(ここではmyFirstTriangleVertexBuffer)を作り,
  //最初に作ったGLfloatの配列(ここではmyFirstTriangleVertexArray)をセットして,
  //バッファの番号がmyFirstTriangleVertexBuffer変数に入る
  GLuint myFirstTriangleVertexBuffer;
  glGenBuffers(1, &myFirstTriangleVertexBuffer);
  //GL_ARRAY_BUFFERタグに頂点バッファをバインドする
  glBindBuffer(GL_ARRAY_BUFFER, myFirstTriangleVertexBuffer);
  glBufferData(GL_ARRAY_BUFFER, 
	       sizeof(myFirstTriangleVertexArray), 
	       myFirstTriangleVertexArray, 
	       GL_STATIC_DRAW);
  
  while(!glfwWindowShouldClose(glfwWindow)){
    
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    //実際にシェーダを使って描画する
    glUseProgram(programID);

    
    //gluPerspectiveと同じ役割をする行列を生成する
    glm::mat4 projectionMatrix = 
      glm::perspective(glm::radians(45.0),
		       (double)windowWidth / (double)windowHeight,
		       0.1, 100000.0);
    
    //gluLookAtと同じ役割をする行列を生成する
    glm::mat4 viewMatrix = 
      glm::lookAt(glm::vec3(4.0, 3.0, 3.0), //カメラの位置
		  glm::vec3(0.0, 0.0, 0.0), //カメラが見ている座標は原点
		  glm::vec3(0.0, 1.0, 0.0)); 
    //カメラの位置とカメラが見る座標の線に乗っているカメラの「上」を指定する.
    //通常はY座標だけ1にしておけば良い
    
    glm::mat4 modelMatrix = //glRotate, glTranslate等と同じ役割をする行列を生成する
      glm::mat4(1.0); //単位行列を生成するので,原点のまま
    
    //glTranslateと同じ役割をする行列は以下で生成
    //glm::translate(1.0, 0.0, 0.0);

    //glRatateと同じ役割をする行列は以下で生成
    //glm::vec3 rotationAxis(0.0, 0.0, 1.0); //原点から(0, 0, 1)へ向かう軸を作り
    //glm::rotate(60, rotationAxis); //その軸にそって60度回転する
      
    //glm::scale(2.0, 2.0, 2.0); //大きさを2倍にする行列を生成

    //これらの行列をglRotate, glTranslate, glScaleと同じ順に掛け算してって
    //最終的なModelMatrixを生成する
    //glPopMatrixとglPushMatrixの間は処理が逆順になることに注意

    //すべての行列を掛け算すると最終的に座標に適用する行列ができる
    glm::mat4 finalModelViewProjectionMatrix = 
      projectionMatrix * viewMatrix * modelMatrix;

    //頂点シェーダのプログラムに渡す
    //頂点シェーダプログラムのuniform mat4型変数"finalMVP"のIDを取り出す
    GLuint matrixID = glGetUniformLocation(programID, "finalMVP");
    //取り出した頂点シェーダプログラム内のmat4型変数finalMVPに,
    //mainプログラム中のfinalModelViewProjection行列を渡す
    glUniformMatrix4fv(matrixID, 
		       1, 
		       GL_FALSE, 
		       &finalModelViewProjectionMatrix[0][0]);

    //頂点を描画する
    //頂点シェーダプログラムの
    //attribute vec3型変数"vertexPositionFromMain"のIDを取り出す
    GLint vertexAttributeID = 
      glGetAttribLocation(programID, "vertexPositionFromMain");
    //頂点描画モードをオンにする
    glEnableVertexAttribArray(vertexAttributeID);
    //初期化した頂点バッファの番号が入っているバッファをバインドする
    glBindBuffer(GL_ARRAY_BUFFER, myFirstTriangleVertexBuffer); 
    //頂点情報を転送する
    glVertexAttribPointer(vertexAttributeID,
			  3, //頂点数
			  GL_FLOAT, //頂点を指定した型floatで問題無い
			  GL_FALSE, //正規化をしない
			  0, //ストライド
			  (void*)0); //配列バッファオブジェクト
    //描く!
    glDrawArrays(GL_TRIANGLES, 0, 3); //頂点0から合計3つの頂点を描く.三角形型.
 
    //頂点描画モードをオフにする
    glDisableVertexAttribArray(vertexAttributeID);

    glfwSwapBuffers(glfwWindow);
    glfwPollEvents();
  }

  //GL_ARRAY_BUFFERにバインドされたバッファオブジェクトのバインドを解除
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  //頂点バッファを破棄
  glDeleteBuffers(1, &myFirstTriangleVertexBuffer);
}

//シェーダを読み込むプログラム
//大して理解する必要のない内容なので,これはこのまま使って良い
GLuint loadShaders(const char* vertex_file_path,
		   const char* fragment_file_path){
  // シェーダを表すIDを作る
  GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
  GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

  // ファイルから頂点シェーダのコードを読み込み
  std::string VertexShaderCode;
  std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
  if(VertexShaderStream.is_open()){
    std::string Line = "";
    while(getline(VertexShaderStream, Line))
      VertexShaderCode += "\n" + Line;
    VertexShaderStream.close();
  }
  
  // ファイルからフラグメントシェーダを読み込み
  std::string FragmentShaderCode;
  std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
  if(FragmentShaderStream.is_open()){
    std::string Line = "";
    while(getline(FragmentShaderStream, Line))
      FragmentShaderCode += "\n" + Line;
    FragmentShaderStream.close();
  }
  
  GLint Result = GL_FALSE;
  int InfoLogLength;
  
  // 頂点シェーダをコンパイル
  printf("Compiling shader : %s\n", vertex_file_path);
  char const * VertexSourcePointer = VertexShaderCode.c_str();
  glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
  glCompileShader(VertexShaderID);
  
  // 頂点シェーダをチェック
  glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
  glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  if(Result == GL_FALSE){
    std::vector VertexShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
    fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
    exit(1);
  }
  
  // フラグメントシェーダをコンパイル
  printf("Compiling shader : %s\n", fragment_file_path);
  char const * FragmentSourcePointer = FragmentShaderCode.c_str();
  glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
  glCompileShader(FragmentShaderID);
  
  // フラグメントシェーダをチェック
  glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
  if(Result == GL_FALSE){
    glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector FragmentShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
    fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);
    exit(1);
  }
  
  // プログラムをリンク
  fprintf(stdout, "Linking program\n");
  GLuint ProgramID = glCreateProgram();
  glAttachShader(ProgramID, VertexShaderID);
  glAttachShader(ProgramID, FragmentShaderID);
  glLinkProgram(ProgramID);
  
  // プログラムをチェック
  fprintf(stdout, "Checking program\n");
  glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
  if(Result == GL_FALSE){
    glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector ProgramErrorMessage( std::max(InfoLogLength, int(1)) );
    glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
    exit(1);
  }
  fprintf(stdout, "Deleting Shader ID\n");
  glDeleteShader(VertexShaderID);
  glDeleteShader(FragmentShaderID);

  fprintf(stdout, "Finish load, compile, bind shader\n");  
  return ProgramID;
}
// GenerateMatrixVertexShader_1_VertexShader.glsl
// written in GLSL1.5 for OpenGL3.2
attribute vec3 vertexPositionFromMain; //元々の頂点座標

//main関数から渡される行列
uniform mat4 finalMVP;

void main(){
  //頂点のアウトプット座標は,最終的な変換行列 * 元々の座標
  //元々の座標は3次元なのだが,行列演算するために4次元vectorにする
  //意味合いはアフィン変換と同じような感じ

  //vec4 v = vec4(vertexPositionFromMain, 1);
  //4次元vectorにしたら最終的な変換行列とかける
  vec4 v = vec4(vertexPositionFromMain.x, 
		vertexPositionFromMain.y,
		vertexPositionFromMain.z,
		1.0);
  //gl_Positionというvec4型変数に値を打ち込むことで,頂点座標の描画が行われる
  gl_Position = finalMVP * v;
}
// GenerateMatrixVertexShader_1_FragmentShader.glsl
// written in GLSL1.5 for OpenGL3.2

void main(){
  //とりあえず色は赤で固定(RGBA)
  //gl_FragColorというvec4型変数に値を打ち込むことでピクセルの色を指定できる
  gl_FragColor = vec4(1.0, 0.0, 0.0, 0.0);
}

色情報も送り込んでみる

// GenerateMatrixVertexShader_2.cpp
#include &lt;GL/glew.h&gt;

#ifdef __APPLE__
#include &lt;OpenGL/opengl.h&gt;
#else
#include &lt;Gl/gl.h&gt;
#endif

#include &lt;GLFW/glfw3.h&gt;
#include &lt;glm/glm.hpp&gt;
#include &lt;glm/gtc/matrix_transform.hpp&gt;


#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;fstream&gt;


//プロトタイプ宣言
GLuint loadShaders(const char* vertex_file_path,
		     const char* fragment_file_path);

//あらかじめ描画する三角形の座標を「1次元配列」として持っておく
//x1, y1, z1, x2, y2, z2, x3, y3, z3
//という感じで並べる
//GLFloatは要するにfloatそのまま
static const GLfloat myFirstTriangleVertexArray[9] = 
  {-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0};
static const GLfloat myTriangleVertexArray1[9] = 
  {0.8, 0.1, 0.0, -0.3, -0.2, 0.0, 0.4, -0.8, 0.0};
static const GLfloat myTriangleVertexArray2[9] = 
  {0.5, 0.0, 0.0, -0.5, -0.3, 0.0, 0.2, -0.4, 0.0};

//あらかじめ描画する色(RGB)の3頂点分を1次元配列として作っておく
static const GLfloat myFirstTriangleColorArray[9] = 
  {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};

int main(void){
  //OpenGL ver.3.2 Core Profileを選択する
  //内容は理解する必要はない
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //OSX用の指定
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  GLFWwindow* glfwWindow;
  if(!glfwInit()){
    return -1;
  }

  int windowWidth = 640;
  int windowHeight = 480;
  glfwWindow = 
    glfwCreateWindow(windowWidth, windowHeight,
		     "GenerateMatrixVertexShader_2",
		     NULL, NULL);
  if(glfwWindow == NULL){
    glfwTerminate();
    return -1;
  }
  
  //作成したウィンドウをOpenGLの処理対象にするContext登録
  glfwMakeContextCurrent(glfwWindow);
  //GLEWを初期化する
  glewExperimental = true;
  if(glewInit() != GLEW_OK){
    fprintf(stderr, "Cannot initialize GLEW\n");
    return -1;
  }

  //配列バッファオブジェクトを生成し,現在のContextに登録する
  GLuint vertexArrayID;
  glGenVertexArrays(1, &vertexArrayID);
  glBindVertexArray(vertexArrayID);

  //シェーダプログラムを使えるようにする
  GLuint programID = 
    loadShaders("GenerateMatrixVertexShader_2_VertexShader.glsl",
		"GenerateMatrixVertexShader_2_FragmentShader.glsl");
  

  //-------------------ここまでは,この順番で行う必要がある--------------------------

  //頂点バッファの初期化 
  //GLfloatの配列から,MainLoop内で描画を実行できる頂点バッファの形を作る作業である
  //モデルごとに GLuint型の変数(ここではmyFirstTriangleVertexBuffer)を作り,
  //最初に作ったGLfloatの配列(ここではmyFirstTriangleVertexArray)をセットして,
  //バッファの番号がmyFirstTriangleVertexBuffer変数に入る
  GLuint myFirstTriangleVertexBuffer;
  glGenBuffers(1, &myFirstTriangleVertexBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, myFirstTriangleVertexBuffer);
  glBufferData(GL_ARRAY_BUFFER, 
	       sizeof(myFirstTriangleVertexArray), 
	       myFirstTriangleVertexArray, 
	       GL_STATIC_DRAW);

  //色付け用のバッファの初期化
  GLuint myFirstTriangleColorBuffer;
  glGenBuffers(1, &myFirstTriangleColorBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, myFirstTriangleColorBuffer);
  glBufferData(GL_ARRAY_BUFFER,
	       sizeof(myFirstTriangleColorArray),
	       myFirstTriangleColorArray,
	       GL_STATIC_DRAW);
  
  
  while(!glfwWindowShouldClose(glfwWindow)){
    
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    //実際にシェーダを使って描画する
    glUseProgram(programID);

    //gluPerspectiveと同じ役割をする行列を生成する
    glm::mat4 projectionMatrix = 
      glm::perspective(glm::radians(45.0),
		       (double)windowWidth / (double)windowHeight,
		       0.1, 100000.0);
    
    //gluLookAtと同じ役割をする行列を生成する
    glm::mat4 viewMatrix = 
      glm::lookAt(glm::vec3(4.0, 3.0, 3.0), //カメラの位置
		  glm::vec3(0.0, 0.0, 0.0), //カメラが見ている座標は原点
		  glm::vec3(0.0, 1.0, 0.0)); 
    //カメラの位置とカメラが見る座標の線に乗っているカメラの「上」を指定する.
    //通常はY座標だけ1にしておけば良い
    
    // ------モデルの描画はここから-------------------------
    glm::mat4 modelMatrix = //glRotate, glTranslate等と同じ役割をする行列を生成する
      glm::mat4(1.0); //単位行列を生成するので,原点のまま
    
    //glTranslateと同じ役割をする行列は以下で生成
    //glm::translate(1.0, 0.0, 0.0);

    //glRatateと同じ役割をする行列は以下で生成
    //glm::vec3 rotationAxis(0.0, 0.0, 1.0); //原点から(0, 0, 1)へ向かう軸を作り
    //glm::rotate(60, rotationAxis); //その軸にそって60度回転する
      
    //glm::scale(2.0, 2.0, 2.0); //大きさを2倍にする行列を生成

    //これらの行列をglRotate, glTranslate, glScaleと同じ順に掛け算してって
    //最終的なModelMatrixを生成する
    //glPopMatrixとglPushMatrixの間は処理が逆順になることに注意

    //すべての行列を掛け算すると最終的に座標に適用する行列ができる
    glm::mat4 finalModelViewProjectionMatrix = 
      projectionMatrix * viewMatrix * modelMatrix;



    //頂点シェーダのプログラムに渡す
    //頂点シェーダープログラムのuniform mat4型変数 "finalMVP"を取り出す
    GLuint matrixID = glGetUniformLocation(programID, "finalMVP");
    //取り出した頂点シェーダプログラム内のmat4型変数finalMVPに,
    //mainプログラム中のfinalModelViewProjection行列を渡す
    glUniformMatrix4fv(matrixID, 
		       1, 
		       GL_FALSE, 
		       &finalModelViewProjectionMatrix[0][0]);
    
    //頂点シェーダのプログラムに色情報を渡す
    //頂点シェーダプログラムのattribute vec4型変数 "colorFromMain"を取り出す
    GLint colorAttributeID = 
      glGetAttribLocation(programID, "colorFromMain");
    glEnableVertexAttribArray(colorAttributeID);
    glBindBuffer(GL_ARRAY_BUFFER, myFirstTriangleColorBuffer);
    glVertexAttribPointer(colorAttributeID,
			  3,
			  GL_FLOAT,
			  GL_FALSE,
			  0,
			  (void*)0);

    //頂点シェーダのプログラムに頂点座標を渡す
    GLint vertexAttributeID = 
      glGetAttribLocation(programID, "vertexPositionFromMain");
    //頂点描画モードをオンにする
    glEnableVertexAttribArray(vertexAttributeID);
    //初期化した頂点バッファの番号が入っているバッファをバインドする
    glBindBuffer(GL_ARRAY_BUFFER, myFirstTriangleVertexBuffer); 
    //頂点情報を転送する
    glVertexAttribPointer(vertexAttributeID, 
			  3, //頂点数
			  GL_FLOAT, //頂点を指定した型floatで問題無い
			  GL_FALSE, //正規化をしない
			  0, //ストライド
			  (void*)0); //配列バッファオブジェクト
    //描く!
    glDrawArrays(GL_TRIANGLES, 0, 3); //頂点0から合計3つの頂点を描く.三角形型.

    //頂点描画モードをオフにする
    glDisableVertexAttribArray(colorAttributeID);
    glDisableVertexAttribArray(vertexAttributeID);


    glfwSwapBuffers(glfwWindow);
    glfwPollEvents();
  }
  //バッファオブジェクトを解放する
  glBindBuffer(GL_ARRAY_BUFFER, 0);
}

//シェーダを読み込むプログラム
//大して理解する必要のない内容なので,これはこのまま使って良い
GLuint loadShaders(const char* vertex_file_path,
		   const char* fragment_file_path){
  // シェーダを作ります。
  GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
  GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);


  // ファイルから頂点シェーダのコードを読み込みます。
  std::string VertexShaderCode;
  std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
  if(VertexShaderStream.is_open()){
    std::string Line = "";
    while(getline(VertexShaderStream, Line))
      VertexShaderCode += "\n" + Line;
    VertexShaderStream.close();
  }
  
  // ファイルからフラグメントシェーダを読み込みます。
  std::string FragmentShaderCode;
  std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
  if(FragmentShaderStream.is_open()){
    std::string Line = "";
    while(getline(FragmentShaderStream, Line))
      FragmentShaderCode += "\n" + Line;
    FragmentShaderStream.close();
  }
  
  GLint Result = GL_FALSE;
  int InfoLogLength;
  
  // 頂点シェーダをコンパイルします。
  printf("Compiling shader : %s\n", vertex_file_path);
  char const * VertexSourcePointer = VertexShaderCode.c_str();
  glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
  glCompileShader(VertexShaderID);
  
  // 頂点シェーダをチェックします。
  glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
  glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  if(Result == GL_FALSE){
    std::vector&lt;GLchar&gt; VertexShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
    fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
    exit(1);
  }
  
  // フラグメントシェーダをコンパイルします。
  printf("Compiling shader : %s\n", fragment_file_path);
  char const * FragmentSourcePointer = FragmentShaderCode.c_str();
  glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
  glCompileShader(FragmentShaderID);
  
  // フラグメントシェーダをチェックします。
  glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
  if(Result == GL_FALSE){
    glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector&lt;GLchar&gt; FragmentShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
    fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);
    exit(1);
  }
  
  // プログラムをリンクします。
  fprintf(stdout, "Linking program\n");
  GLuint ProgramID = glCreateProgram();
  glAttachShader(ProgramID, VertexShaderID);
  glAttachShader(ProgramID, FragmentShaderID);
  glLinkProgram(ProgramID);
  
  // プログラムをチェックします。
  fprintf(stdout, "Checking program\n");
  glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
  if(Result == GL_FALSE){
    glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector&lt;GLchar&gt; ProgramErrorMessage( std::max(InfoLogLength, int(1)) );
    glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
    exit(1);
  }
  fprintf(stdout, "Deleting Shader ID\n");
  glDeleteShader(VertexShaderID);
  glDeleteShader(FragmentShaderID);

  fprintf(stdout, "Finish load, compile, bind shader\n");
  return ProgramID;
}
// GenerateMatrixVertexShader_2_VertexShader.glsl
// written in GLSL1.5 for OpenGL3.2
attribute vec3 vertexPositionFromMain; //元々の頂点座標
// OpenGL3.3の倍は,上の1行を下の2行で置き換える
//#version 330 core
//layout(location = 0) in vec3 vertexPositionFromMain; 

//色の情報もフラグメントシェーダに直接渡さず,頂点シェーダを経由するため
//まずmainプログラムから受け取る変数を定義して,
attribute vec3 vertexColorFromMain;
//上の値をフラグメントシェーダに渡すための変数も用意する
//フラグメントシェーダ側でも宣言されている.
varying vec4 vertexColorInShader; 

//main関数から渡される行列
uniform mat4 finalMVP;

void main(){
  //頂点のアウトプット座標は,最終的な変換行列 * 元々の座標
  //元々の座標は3次元なのだが,行列演算するために4次元vectorにする
  //意味合いはアフィン変換と同じような感じ

  //vec4 v = vec4(vertexPositionFromMain, 1);
  //4次元vectorにしたら最終的な変換行列とかける
  vec4 v = vec4(vertexPositionFromMain.x, 
		vertexPositionFromMain.y,
		vertexPositionFromMain.z,
		1.0);
  gl_Position = finalMVP * v;

  //フラグメントシェーダへ色の引き渡しを行う
  vertexColorInShader = 
    vec4(colorFromMain.x, colorFromMain.y, colorFromMain.z, 0.0);
}
// written in GLSL1.5 for OpenGL3.2
// OpenGL3.3の場合は,以下の2行の宣言が必要となる.
//#version 330 core
//out vec3 outputColorFromShader; //アウトプットデータ

//頂点シェーダから渡される値
//頂点シェーダ側でも同じ名前で宣言されている
varying vec4 vertexColorInShader;

void main(){
  // 頂点に設定されていた色(という名目で受け渡された色)をそのまま最終的な色にする
  gl_FragColor = vertexColorInShader;
}

glDrawArraysではなくglDrawElementsを用いる

今まで描画する全ての三角形ポリゴンの3点座標のx, y zを,頂点順に入力した配列をglDrawArrays()関数で描画してきた.この方法では頂点の数が「ポリゴンの数の3倍」になるのは自明である.

ところが描画するポリゴンが増えてくると,ポリゴンとポリゴンの間にできる三角形の隙間もポリゴンに自動的にならないか,という欲求が出てくる.すなわち「頂点の座標だけバラバラに指定して,もしその頂点で三角形が作れるのであれば,その間を勝手にポリゴンで埋めてほしい」という欲求である.これに対応するのがglDrawElements()関数である.

指定するデータの形を見てみる.

glDrawArraysとglDrawElementsで指定するデータ構造の違い.glDrawArraysは全てのポリゴンの座標情報が必要なのに対して,glDrawElementsは「頂点の情報」と「頂点番号の配列」に分けて保持することで,データ量の削減を図っている.

さすがに「勝手に埋める」ことはできないが,glDrawArraysは全てのポリゴンの座標情報が必要なのに対して,glDrawElementsは「頂点の情報」と「頂点番号の配列」に分けて保持することで,データ量の削減を図っている.これによりポリゴンの頂点が被っている場合などは頂点座標情報を省略できるため,データ量が大幅に削減できる.