さて、理解のための動作例はできたので、新しい機能やさらなる堅牢性の追加にとりかかれます。 
以前のシェーダーは、できるだけ簡単になるように意図的に最小限にしていましたが、実世界の例ではもっと付属コードを使用します。 
最初に追加するものは、シェーダをロードするためのより便利な方法です：外部ファイルのロードがとても簡単になるはずです（Cの文字列としてコードにコピーペーストするよりも）。  それに加えて、こうすることでCコードを再コンパイルせずにGLSLコードを変更できるようになります！ 
まず、ファイルを文字列としてロードする関数が必要になります。 
基本的なC言語のコードで、ファイルのサイズに割り当てられたバッファの中にファイルの内容を読み取ります。 
/**
 * Store all the file's contents in memory, useful to pass shaders
 * source code to OpenGL
 */
/* Problem:
 *  We should close the input file before the return NULL; statements but this would lead to a lot of repetition (DRY)
 *   -you could solve this by using goto or by abusing switch/for/while + break or by building an if else mess
 *  better solution: let the user handle the File: char* file_read(const FILE* input)
char* file_read(const char* filename)
{
  FILE* input = fopen(filename, "rb");
  if(input == NULL) return NULL;
  
  if(fseek(input, 0, SEEK_END) == -1) return NULL;
  long size = ftell(input);
  if(size == -1) return NULL;
  if(fseek(input, 0, SEEK_SET) == -1) return NULL;
  	
  /*if using c-compiler: dont cast malloc's return value*/
  char *content = (char*) malloc( (size_t) size +1  ); 
  if(content == NULL) return NULL;
  
  fread(content, 1, (size_t)size, input);
  if(ferror(input)) {
    free(content);
    return NULL;
  }
  fclose(input);
  content[size] = '\0';
  return content;
}
現時点でシェーダにエラーがある場合、プログラムは何のエラーかを特に説明することなく、ただ停止します。 
''infolog''を使用することで、OpenGLからより多くの情報を得ることができます：
/**
 * Display compilation errors from the OpenGL shader compiler
 */
void print_log(GLuint object)
{
  GLint log_length = 0;
  if (glIsShader(object))
    glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
  else if (glIsProgram(object))
    glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
  else {
    fprintf(stderr, "printlog: Not a shader or a program\n");
    return;
  }
  char* log = (char*)malloc(log_length);
  if (glIsShader(object))
    glGetShaderInfoLog(object, log_length, NULL, log);
  else if (glIsProgram(object))
    glGetProgramInfoLog(object, log_length, NULL, log);
  fprintf(stderr, "%s", log);
  free(log);
}
GLES2関数だけを使用する場合、アプリケーションはデスクトップとモバイルデバイスの両方にほぼ移植可能です。 
対処する問題がまだひと組あります： 
The #version needs to be the very first line in some GLSL compilers (for instance on the PowerVR SGX540), so we cannot use #ifdef directives to abstract it in the GLSL shader. Instead, we'll prepend the version in the C++ code:
  const GLchar* sources[2] = {
    "#version 100\n"
    // Note: OpenGL ES automatically defines this:
    // #define GL_ES
    "#version 120\n",
    source };
  glShaderSource(res, 2, sources, NULL);
私たちはすべてのチュートリアルで同じバージョンのGLSLを使用するため、これが最も簡単な解決策です。 
これらの新しいユーティリティ関数と知識を使って、シェーダのロードとデバッグのための別の関数を作ることができます： 
/**
 * Compile the shader from file 'filename', with error handling
 */
GLuint create_shader(const char* filename, GLenum type)
{
  const GLchar* source = file_read(filename);
  if (source == NULL) {
    fprintf(stderr, "Error opening %s: ", filename); perror("");
    return 0;
  }
  GLuint res = glCreateShader(type);
  const GLchar* sources[2] = {
    "#version 100\n"
    "#define GLES2\n",
    "#version 120\n",
    source };
  glShaderSource(res, 2, sources, NULL);
  free((void*)source);
  glCompileShader(res);
  GLint compile_ok = GL_FALSE;
  glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
  if (compile_ok == GL_FALSE) {
    fprintf(stderr, "%s:", filename);
    print_log(res);
    glDeleteShader(res);
    return 0;
  }
  return res;
}
これでもうシェーダーをシンプルに使用してコンパイルできます：  
  GLuint vs, fs;
  if ((vs = create_shader("triangle.v.glsl", GL_VERTEX_SHADER))   == 0) return 0;
  if ((fs = create_shader("triangle.f.glsl", GL_FRAGMENT_SHADER)) == 0) return 0;
同様にリンクエラーも表示します： 
  if (!link_ok) {
    fprintf(stderr, "glLinkProgram:");
    print_log(program);
これらの新しい関数を、  shader_utils.cppに配置します。 
これらの関数の記述を可能な限り少なくしようとしている点にお気づきでしょうか： OpenGLWikibookの目標は、OpenGLがどのように動作するかを理解することであって、開発しているツールキットの使用方法を理解することではありません。 
shader_utils.hヘッダファイルを作成しましょう： 
char* file_read(const char* filename);
void print_log(GLuint object);
GLuint create_shader(const char* filename, GLenum type);
triangle.cpp内の新しいファイルを参照 ： 
そして Makefileの中で ： 
triangle: shader_utils.o
頂点バッファオブジェクト（VBO）を使用して、グラフィックカードに頂点を直接格納することは、良い習慣です。 
そのうえ、 "client-side arrays"のサポートはOpenGL 3.0以降は公式に削除され、WebGLには存在せず、そして低速なので、少し単純さには欠けても、今のうちからVBOを使用してみましょう。   たまたま出くわした既存のOpenGLコードで使用されていたりするかもしれないですから、両方の方法について知っておくことが重要です。 
2つ​​のステップでこれを実装します： 
グローバル変数を作成  (#includeの下) してVBOハンドルを格納する： 
GLuint vbo_triangle;
init_resourcesで、 triangle_verticesを定義しているところに移動して、ひとつ（1）のデータバッファを作成し、それを現在のアクティブなバッファにします： 
  GLfloat triangle_vertices[] = {
     0.0,  0.8,
    -0.8, -0.8,
     0.8, -0.8,
  };
  glGenBuffers(1, &vbo_triangle);
  glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle);
さて、このバッファに頂点をプッシュすることができます。  データの編成方法や使用される頻度を指定します。 
GL_STATIC_DRAW は、このバッファにはあまり頻繁に書き込みはせず、そしてGPUが自身のメモリ内にコピーを保持する必要があるということを示しています。  VBOに新しい値を書き込むことは常に可能です。フレームあたりに1度かそれ以上頻繁にデータを変更する場合は、GL_DYNAMIC_DRAWまたはGL_STREAM_DRAWを使用することができます。 
  glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
どんな時でも、アクティブなバッファの設定解除を、このように行えます： 
  glBindBuffer(GL_ARRAY_BUFFER, 0);
とりわけ、C言語の配列を常に直接渡す必要がある場合は、アクティブバッファを無効にしていることを確認してください。 
onDisplayで、コードを少し適合させます。 
glBindBufferを呼び出して、  glVertexAttribPointerの最後の2つのパラメータを変更します ： 
  glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle);
  glEnableVertexAttribArray(attribute_coord2d);
  /* Describe our vertices array to OpenGL (it can't guess its format automatically) */
  glVertexAttribPointer(
    attribute_coord2d, // attribute
    2,                 // number of elements per vertex, here (x,y)
    GL_FLOAT,          // the type of each element
    GL_FALSE,          // take our values as-is
    0,                 // no extra data between each position
    0                  // offset of first element
  );
終了時のクリーンアップを忘れずにやっておきましょう： 
void free_resources()
{
  glDeleteProgram(program);
  glDeleteBuffers(1, &vbo_triangle);
}
今はもう、シーンを描画するたびに、OpenGLがすべての頂点をGPU側で既に持っていることになります。   数千ポリゴンもの大きなシーンの場合、これは莫大なスピードアップになりえます。 
一部のユーザーが持っているグラフィックカードは、OpenGL 2をサポートしていない可能性があります。 
これはおそらくプログラムをクラッシュさせたり、不完全なシーンを表示したり、といったことにつながります。 
GLEWを使用してこれを確認することができます（glewInit（）の呼び出しが成功した後に）： 
  if (!GLEW_VERSION_2_0) {
    fprintf(stderr, "Error: your graphic card does not support OpenGL 2.0\n");
    return 1;
  }
注意点として、一部のチュートリアルは2.0付近のカードでしか動作しないこともあり、たとえばIntel 945GMが公式でサポートしているOpenGL 1.4以外はシェーダのサポートが限定的です。 
他のOpenGLのコードの中で、次のようなヘッダーに出会うかもしれません： 
OpenGLの拡張機能をロードする必要がない場合で、 ''かつ''ヘッダが十分に最近のものである場合は、GLEWの代わりにこれを使用することができます。 
私たちのテストによると、Windowsユーザーは古いヘッダを持っているかもしれず、GL_VERTEX_SHADERなどのようなシンボルを見失ってしまうので、これらのチュートリアルではGLEWを使用します（プラス私たちが拡張をロードするときのための準備にもなります）。 
GLEWとGLeeの比較については、 APIs, Libraries and acronyms セクションもご覧ください。 
ユーザーの報告によると、Intel 945GM GPUのGLEWのかわりにこのテクニックを使用することで、シンプルなチュートリアルでは不完全なOpenGL 2.0サポートをバイパスすることができたそうです。  GLEW自体は部分的なサポートを有効にすることもでき、glutInitを呼び出す前に glewExperimental = GL_TRUE;を追加することで行えます。 
今私たちのプログラムはさらにメンテナンスしやすくなっていますが、やっていることは以前とまったく同じです！ 
そういうわけで透明度でちょっと実験しながら、  "昔のテレビ "のエフェクトをつけて三角形を表示してみましょう。 
まず、GLUTをアルファといっしょに初期化します： 
 glutInitDisplayMode(GLUT_RGBA|GLUT_ALPHA|GLUT_DOUBLE|GLUT_DEPTH);
その後、OpenGLで透明度（デフォルトではオフです）を明示的に有効にします： 
// Enable alpha
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
thumb|150px|The rendered triangle, partially transparent
そして最後に、フラグメントシェーダを変更し、アルファの透明度を定義します： 
  gl_FragColor[0] = gl_FragCoord.x/640.0;
  gl_FragColor[1] = gl_FragCoord.y/480.0;
  gl_FragColor[2] = 0.5;
  gl_FragColor[3] = floor(mod(gl_FragCoord.y, 2.0));
mod と floor は一般的な数学演算子で、偶数か奇数のどちらのラインにいるかの判断に使用しています。 
したがって、2つに1つラインが透明になり、他は不透明になります。 
OpenGL_Programming/Modern_OpenGL_Tutorial_02