2013年7月5日金曜日

サンプル|カラー描画

サンプルです。
本文は、こちらです。

#include <memory>
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>


#include <GL/glew.h>
#define NO_SDL_GLEXT
#include <SDL2/SDL.h>

//OpenGL用の行列変換ライブラリ
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>


#pragma comment(lib,"SDL2.lib")
#pragma comment(lib,"SDL2main.lib")
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"glew32.lib")

namespace sample4 {

class Cube {
public:
  typedef std::shared_ptr<Cube> Ptr;
  Cube(GLfloat size) {
    GLfloat low = -size/2.0f;
    GLfloat high = size/2.0f;
    GLfloat vertices[3*8] = {
      low, low, low,
      low, high, low,
      high, high, low,
      high, low, low,
      low, low, high,
      low, high, high,
      high, high, high,
      high, low, high
    };
    float colors[3*8] = {
      0.0f, 1.0f, 1.0f,
      1.0f, 0.0f, 1.0f,
      1.0f, 1.0f, 0.0f,
      1.0f, 1.0f, 1.0f,
      1.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f,
      0.0f, 0.0f, 1.0f,
      0.0f, 0.0f, 0.0f
    };
    GLuint indecise[6*6] = {
      0, 1, 2, 2, 3, 0,
      4, 7, 6, 6, 5, 4,
      0, 4, 5, 5, 1, 0,
      3, 2, 6, 6, 7, 3,
      1, 5, 6, 6, 2, 1,
      0, 3, 7, 7, 4, 0
    };
    //VAO(バッファデータ・設定群)の生成
    glGenVertexArrays(1, &vertex_array_object_);
    //現在のVAOに設定
    glBindVertexArray(vertex_array_object_);
    //VBO(バッファ)の生成
    glGenBuffers(1, &vertex_buffer_object_);
    //現在のバッファに設定
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object_);
    //現在のバッファにデータを送信
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //データ型の設定(float * 3)
    //glVertexPointer(3, GL_FLOAT, 0, 0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(0);
    //インデックスの設定
    glGenBuffers(1, &index_buffer_object_);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_object_);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indecise), indecise, GL_STATIC_DRAW);
    //カラーバッファの生成
    glGenBuffers(1, &color_buffer_object_);
    glBindBuffer(GL_ARRAY_BUFFER, color_buffer_object_);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(1);
    //VAO設定を終了する
    glBindVertexArray(0);

  }
  ~Cube() {
    glDeleteBuffers(1, &index_buffer_object_);
    glDeleteBuffers(1, &vertex_buffer_object_);
    glDeleteVertexArrays(1, &vertex_array_object_);
  }
  void Draw() {
    glBindVertexArray(vertex_array_object_);
    glDrawElements(GL_TRIANGLES, 6*6, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(0);
  }
private:
  GLuint vertex_array_object_;
  GLuint vertex_buffer_object_;
  GLuint index_buffer_object_;
  GLuint color_buffer_object_;
};

class Effect {
public:
  typedef std::shared_ptr<Effect> Ptr;
  Effect(const std::string& vertex_shader_file, const std::string& fragment_shader_file) {
    Init(vertex_shader_file,fragment_shader_file);
  }
  ~Effect() {
    //各シェーダの利用を終了
    glDetachShader(shader_id_, vertex_shader_id_);
    glDetachShader(shader_id_, fragment_shader_id_);
    //シェーダの破棄
    glDeleteShader(vertex_shader_id_);
    glDeleteShader(fragment_shader_id_);
    //シェーダプログラムの削除
    glDeleteProgram(shader_id_);
  }

  //シェーダの利用を開始する
  void Begin() {
    glUseProgram(shader_id_);
  }

  //シェーダの利用を終了する
  void End() {
    glUseProgram(0);
  }

  //プログラムIDの取得
  GLuint shader_id()const {return shader_id_;}
private:
  void Init(const std::string& vertex_shader_file, const std::string& fragment_shader_file) {
    if (vertex_shader_file.empty() || fragment_shader_file.empty()) {
      return;
    }
    //頂点シェーダの生成
    vertex_shader_id_ = glCreateShader(GL_VERTEX_SHADER);
    //フラグメントシェーダの生成
    fragment_shader_id_ = glCreateShader(GL_FRAGMENT_SHADER);
    //ソースコードの読み込み
    std::string vertex_source = ReadFile(vertex_shader_file);
    std::string fragment_source = ReadFile(fragment_shader_file);
    const char* v_source = vertex_source.c_str();
    const char* f_source = fragment_source.c_str();
    if (vertex_source.empty() || fragment_source.empty()) {
      return;
    }
    //シェーダとソースをバインド
    glShaderSource(vertex_shader_id_, 1, &v_source, 0);
    glShaderSource(fragment_shader_id_, 1, &f_source, 0);
    //シェーダをコンパイル
    glCompileShader(vertex_shader_id_);
    glCompileShader(fragment_shader_id_);
    //シェーダプログラムの生成
    shader_id_ = glCreateProgram();
    //利用するシェーダを登録
    glAttachShader(shader_id_, vertex_shader_id_);
    glAttachShader(shader_id_, fragment_shader_id_);
    //シェーダの入力変数のメモリ位置を指定
    glBindAttribLocation(shader_id_, 0, "vertex");
    //各シェーダの接続
    glLinkProgram(shader_id_);
  }
  std::string ReadFile(const std::string& filename) {
    std::ifstream ifs(filename, std::ios::in);
    if(!ifs)return nullptr;
    std::istreambuf_iterator<char> ifs_begin(ifs);
    std::istreambuf_iterator<char> ifs_end;
    std::string file_string(ifs_begin,ifs_end);
    return file_string;
  }

  GLuint shader_id_;
  GLuint vertex_shader_id_;
  GLuint fragment_shader_id_;
};

class Sample4 {
public:
  Sample4() {
    window_ = nullptr;
  }
  ~Sample4() {
    UnInit();
  }

  bool Run() {
    if (!Init()) return false;
    if (!InitRenderObjects()) return false;
    while (PollEvent()) {
      Render();
    }
    return true;
  }

private:
  bool Init() {
    //SDLの初期化
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
      std::cerr << "Init video failed.\n";
      return false;
    }
  
    //利用するOpenGLのバージョンを設定
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
    //深度バッファの設定
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    //ダブルバッファの設定
   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    //ウィンドウの生成
    const int WINDOW_X      = 100;
    const int WINDOW_Y      = 100;
    const int WINDOW_WIDTH  = 800;
    const int WINDOW_HEIGHT = 600;
    window_ = SDL_CreateWindow(
      "sample",
      WINDOW_X,
      WINDOW_Y,
      WINDOW_WIDTH,
      WINDOW_HEIGHT,
      SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);

    if(window_ == nullptr) {
      std::cerr << "CreateWindow failed.\n";
      return false;
    }

    //OpenGLコンテキストの生成
    gl_context_ = SDL_GL_CreateContext(window_);
    
    //描画を同期
    SDL_GL_SetSwapInterval(1);
    
    //GLEW(OpenGL拡張)の初期化
    if (glewInit() != GLEW_OK) {
      std::cout << "Init GLEW failed.\n";
      return false;
    }

    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

    return true;
  }

  void UnInit() {
    SDL_Quit();
  }

  bool PollEvent() {
    SDL_Event ev;
    while (SDL_PollEvent(&ev)) {
      if (ev.type == SDL_QUIT) return false;
      if (ev.type == SDL_KEYDOWN &&
          ev.key.keysym.sym == SDLK_F4 &&
          (ev.key.keysym.mod & KMOD_ALT) != 0) {
        return false;
      }
    }
    return true;
  }

  bool InitRenderObjects() {
    shader_ = Effect::Ptr(new Effect("shader.vs","shader.ps"));
    cube_ = Cube::Ptr(new Cube(1.0f));

    //透視投影変換行列を計算
    projection_matrix_ = glm::perspective(
        60.0f,
        static_cast<GLfloat>(800)/static_cast<GLfloat>(600),
        1.0f,
        100.0f);
    //ビュー行列を計算
    view_matrix_ = 
    glm::lookAt( glm::vec3(0.0f, 0.0f, -5.0f), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    //glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.0f));
    //モデル行列を計算
    model_matrix_= glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));

    //シェーダ変数と関連付けるために
    //シェーダ変数のメモリ位置を取得する
    projection_location_ = glGetUniformLocation(shader_->shader_id(), "projection_matrix");
    view_location_       = glGetUniformLocation(shader_->shader_id(), "view_matrix");
    model_location_      = glGetUniformLocation(shader_->shader_id(), "model_matrix");


    return true;
  }

  void Render() {
    //画面のクリア
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //シェーダ開始
    shader_->Begin();
    
    view_matrix_ = glm::rotate(view_matrix_, 0.05f, glm::vec3(0.f, 1.f, 0.f));
    view_matrix_ = glm::rotate(view_matrix_, 0.05f, glm::vec3(1.f, 0.f, 0.f));
    //シェーダへデータを送る
    glUniformMatrix4fv(projection_location_, 1, GL_FALSE, &projection_matrix_[0][0]);
    glUniformMatrix4fv(view_location_      , 1, GL_FALSE, &view_matrix_[0][0]);
    glUniformMatrix4fv(model_location_     , 1, GL_FALSE, &model_matrix_[0][0]);

    //図形の描画
    cube_->Draw();

    //シェーダ終了
    shader_->End();

    //画面を表示
    SDL_GL_SwapWindow(window_);
  }

  SDL_Window* window_;
  SDL_GLContext gl_context_;
  Effect::Ptr shader_;
  Cube::Ptr cube_;
  
  glm::mat4 projection_matrix_;
  glm::mat4 view_matrix_;
  glm::mat4 model_matrix_;
  int projection_location_;
  int view_location_;
  int model_location_;
};

}
int main(int argc, char* argv[]) {
  sample4::Sample4 sample;
  sample.Run();
  return 0;
}

カラー描画

初期化

今回はキューブを描画するために、 CULLING(背面消去)と深度テストを有効化しました。

    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

頂点シェーダの変更

#version 150
in vec4 vertex;
in vec3 in_color;
out vec3 pass_color;
uniform mat4 projection_matrix;
uniform mat4 view_matrix;
uniform mat4 model_matrix;

void main(void) {
  gl_Position = projection_matrix * view_matrix * model_matrix * vertex;
  pass_color = in_color;
}

入力変数にin_colorが増えています。
頂点シェーダの入力ですので、頂点情報vertexと同じように OpenGLへデータを渡すことができます。
変数は上から順にindexが割り振られますので、 vertexが0、in_colorが1となり、 glVertexAttribPointerの第一引数に指定します。

キューブ描画クラス

class Cube {
public:
  typedef std::shared_ptr Ptr;
  Cube(GLfloat size) {
    GLfloat low = -size/2.0f;
    GLfloat high = size/2.0f;
    GLfloat vertices[3*8] = {
      low, low, low,
      low, high, low,
      high, high, low,
      high, low, low,
      low, low, high,
      low, high, high,
      high, high, high,
      high, low, high
    };
    float colors[3*8] = {
      0.0f, 1.0f, 1.0f,
      1.0f, 0.0f, 1.0f,
      1.0f, 1.0f, 0.0f,
      1.0f, 1.0f, 1.0f,
      1.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f,
      0.0f, 0.0f, 1.0f,
      0.0f, 0.0f, 0.0f
    };
    GLuint indecise[6*6] = {
      0, 1, 2, 2, 3, 0,
      4, 7, 6, 6, 5, 4,
      0, 4, 5, 5, 1, 0,
      3, 2, 6, 6, 7, 3,
      1, 5, 6, 6, 2, 1,
      0, 3, 7, 7, 4, 0
    };
    //VAO(バッファデータ・設定群)の生成
    glGenVertexArrays(1, &vertex_array_object_);
    //現在のVAOに設定
    glBindVertexArray(vertex_array_object_);
    //VBO(バッファ)の生成
    glGenBuffers(1, &vertex_buffer_object_);
    //現在のバッファに設定
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object_);
    //現在のバッファにデータを送信
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //データ型の設定(float * 3)
    //glVertexPointer(3, GL_FLOAT, 0, 0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(0);
    //インデックスの設定
    glGenBuffers(1, &index_buffer_object_);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_object_);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indecise), indecise, GL_STATIC_DRAW);
    //カラーバッファの生成
    glGenBuffers(1, &color_buffer_object_);
    glBindBuffer(GL_ARRAY_BUFFER, color_buffer_object_);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(1);
    //VAO設定を終了する
    glBindVertexArray(0);

  }
  ~Cube() {
    glDeleteBuffers(1, &index_buffer_object_);
    glDeleteBuffers(1, &vertex_buffer_object_);
    glDeleteVertexArrays(1, &vertex_array_object_);
  }
  void Draw() {
    glBindVertexArray(vertex_array_object_);
    glDrawElements(GL_TRIANGLES, 6*6, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(0);
  }
private:
  GLuint vertex_array_object_;
  GLuint vertex_buffer_object_;
  GLuint index_buffer_object_;
  GLuint color_buffer_object_;
};

基本的にはカラーバッファの生成が増えただけです。
ここでのポイントは、glVertexAttribPointerindex=1を指定することと、 glEnableVertexAttribArrayindex=1を有効化することです。


今回のサンプルはこちらです。

2013年6月27日木曜日

座標変換とシェーダ

追加のライブラリ

glm:OpenGL用の行列演算を行うヘッダライブラリ

追加のヘッダファイル

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

行列の変数とシェーダでのメモリ位置

シェーダソースコード内の変数の位置を指定する

  //利用するシェーダを登録
  glAttachShader(shader_id_, vertex_shader_id_);
  glAttachShader(shader_id_, fragment_shader_id_);
  .
  .
  .
  //シェーダの入力変数のメモリ位置を指定
  glBindAttribLocation(shader_id_, 0, "vertex");
  .
  .
  .
  
  //各シェーダの接続
  glLinkProgram(shader_id_);

座標変換に必要な行列の準備とシェーダでのメモリ位置を取得する

  glm::mat4 projection_matrix_;
  glm::mat4 view_matrix_;
  glm::mat4 model_matrix_;
  int projection_location_;
  int view_location_;
  int model_location_;

  //透視投影変換行列を計算
  projection_matrix_ = glm::perspective(
      60.0f,
      static_cast<GLfloat>(800)/static_cast<GLfloat>(600),
      1.0f,
      100.0f);
  //ビュー行列を計算
  view_matrix_ = 
  glm::lookAt( glm::vec3(0.0f, 0.0f, -5.0f), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
  //glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.0f));
  //モデル行列を計算
  model_matrix_= glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));

  //シェーダ変数と関連付けるために
  //シェーダ変数のメモリ位置を取得する
  projection_location_ = glGetUniformLocation(shader_->shader_id(), "projection_matrix");
  view_location_       = glGetUniformLocation(shader_->shader_id(), "view_matrix");
  model_location_      = glGetUniformLocation(shader_->shader_id(), "model_matrix");

描画処理

  //画面のクリア
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  //シェーダ開始
  shader_->Begin();

  //ビュー行列の回転
  view_matrix_ = glm::rotate(view_matrix_, 0.1f, glm::vec3(0.f, 1.f, 0.f));
  //シェーダへデータを送る
  glUniformMatrix4fv(projection_location_, 1, GL_FALSE, &projection_matrix_[0][0]);
  glUniformMatrix4fv(view_location_      , 1, GL_FALSE, &view_matrix_[0][0]);
  glUniformMatrix4fv(model_location_     , 1, GL_FALSE, &model_matrix_[0][0]);

  //図形の描画
  triangle_->Draw();

  //シェーダ終了
  shader_->End();

  //画面を表示
  SDL_GL_SwapWindow(window_);

シェーダ

shader.vs

#version 150
in vec4 vertex;
out vec3 pass_color;
uniform mat4 projection_matrix;
uniform mat4 view_matrix;
uniform mat4 model_matrix;

void main(void) {
  gl_Position = projection_matrix * view_matrix * model_matrix * vertex;
  pass_color = vec3(1.0f);
}

shader.ps

#version 150
in vec3 pass_color;
out vec4 out_color;
void main(void) {
  out_color = vec4(pass_color, 1.0);
}

最初にglBindAttribLocation(shader_id_, 0, "vertex")で シェーダプログラム内の変数vertexの位置を0に指定します。
この位置はglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr)の第一引数と 連動します。

次にglmの関数を用いて座標変換行列を生成しています。(ここでは詳細を割愛)
そして、glGetUniformLocation(shader_->shader_id(), "projection_matrix")で、 shader.vs内のprojection_matrix変数の位置を取得します。
この位置を利用して、描画ごとに glUniformMatrix4fv(projection_location_, 1, GL_FALSE, &projection_matrix_[0][0]) とすることで、行列をシェーダへ転送します。

シェーダプログラムの一行目#versioin 150は、 GLSLのバージョン1.50を利用する宣言です。
これを宣言しない場合には1.10が利用されるようです。
二行目のin vec4 vertexinvertexが入力変数であることを示しています。
三行目で指定しているoutは次のシェーダへ渡される変数であることを示します。
また、uniformは、外部から渡される定数であることを示し、 値はglUniformMatrix4fvで指定しています。

2013年6月24日月曜日

三角形の描画

三角形を描画します。
glBegin,glEnd等を使って、直接描画することができないので、
VAOとVBOを生成して、描画します。

VAO(Vertex Array Object)とは

描画に用いるバッファやバッファの設定の集まりです。
1つのVAOには複数のVBOを含みます。
初期化時にglBindVertexArrayを呼び出して、
VAOの設定をし、描画時にもう一度呼び出すと、設定が全てロードされるので
描画処理を単純化することができます。

VBO(Vertex Buffer Object)とは

描画に用いるバッファ(つまりデータ)です。
VBOには2種類あり、GL_ARRAY_BUFFERGL_ELEMENT_ARRAY_BUFFERがあります。
GL_ARRAY_BUFFERは、頂点データやテクスチャ座標、法線データなど通常のバッファです。
GL_ELEMENT_ARRAY_BUFFERは、描画時の頂点の描画する順番のインデックスを格納します。
また、これらのバッファはglVertexAttribPointerを使って、シェーダへ渡します。

三角形描画クラス

Sample1のソースコードに以下のクラスを追加すると利用できます。
初期化処理などは以前の記事を参考にしてください。
class Triangle {
public:
  typedef std::shared_ptr Ptr;
  Triangle(GLfloat size) {
    GLfloat low = -size/2.0f;
    GLfloat high = size/2.0f;
    GLfloat vertices[3*3] = {
      low, low, low,
      low, high, low,
      high, high, low,
    };
    GLuint indecise[ELEMENT_SIZE] = {
      0, 1, 2
    };
    //VAO(バッファデータ・設定群)の生成
    glGenVertexArrays(1, &vertex_array_object_);
    //現在のVAOに設定
    glBindVertexArray(vertex_array_object_);
    //VBO(バッファ)の生成
    glGenBuffers(1, &vertex_buffer_object_);
    //現在のバッファに設定
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object_);
    //現在のバッファにデータを送信
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //データ型の設定(float * 3)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    //シェーダで0番地を使用
    glEnableVertexAttribArray(0);
    //インデックスの設定
    glGenBuffers(1, &index_buffer_object_);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_object_);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indecise), indecise, GL_STATIC_DRAW);
    //VAO設定を終了する
    glBindVertexArray(0);
  }

  ~Triangle() {
    glDeleteBuffers(1, &index_buffer_object_);
    glDeleteBuffers(1, &vertex_buffer_object_);
    glDeleteVertexArrays(1, &vertex_array_object_);
  }
  
  void Draw() {
    //VAOの有効化(VAOに割り当てた設定とバッファが復元される)
    glBindVertexArray(vertex_array_object_);
    //インデックスを用いて描画する
    glDrawElements(GL_TRIANGLES, ELEMENT_SIZE, GL_UNSIGNED_INT, nullptr);
    //VAOの無効化
    glBindVertexArray(0);
  }
private:
  enum { ELEMENT_SIZE = 3};
  GLuint vertex_array_object_;
  GLuint vertex_buffer_object_;
  GLuint index_buffer_object_;
};
VAO生成→VAOのバインド→{VBOの設定}→VAOの無効化がVAO設定の流れになります。
VBOの設定ではVBOの生成→VBOのバインド→データ送信→(シェーダへの設定)となります。
glGenVertexArrays(num, *buffers):num個のVAOを生成します。
glBindVertexArray(buffer):現在のVAOを設定します。
glGenBuffers(num, *buffers):num個のVBOを生成します。
glBindBuffer(target, buffer):現在のVBOを設定します。
targetには、頂点データのバッファGL_ARRAY_BUFFERやインデックスバッファGL_ELEMENT_ARRAY_BUFFERがあります。
glBufferData(target, size, *data, usage):VBOにデータを送信します。
usageはGL_XXXX_YYYYの形式で表されます。
(XXXX)
  • STREAM 読み込みも書き込みも一度のみのバッファ
  • STATIC 書き込みが一度だけで、読み込みは何度も行われるバッファ
  • DYNAMIC 何度も読み書きが行われるバッファ
  • (YYYY)
  • DRAW アプリケーション側から書き込まれ、OpenGLが描画のために読み込むバッファ
  • READ OpenGL側で書き込まれ、アプリケーション側で読み込むバッファ
  • COPY OpenGL側で書き込まれ、描画に用いられるバッファ
  • glVertexAttribPointer(index,size,type,normalied,stride,pointer):頂点データの種類とデータ1つ分の大きさを指定し、シェーダのindex番地へと格納します。
    glEnableVertexAttribArray(index):上で設定したデータを利用することを通知します。

    描画は非常に簡単で、glBindVertexArrayで利用するVAOを有効化し、
    描画関数を呼び出し(ここでは、インデックスを利用するglDrawElements)、
    最後に、VAOが汚されないようにglBindVertexArrayでVAOを無効化しておきます。

    ここで一つ注意してほしいことですが、GL_QUADSがサポートされなくなったため、
    四角形を描画する場合は、GL_TRIANGLE_STRIPを使ってください。

    2013年6月23日日曜日

    サンプルコード1

    以前の記事のソースコードです。
    一つのファイルでコンパイルできるようになっています。

    #include <memory>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    
    #include <GL/glew.h>
    #define NO_SDL_GLEXT
    #include <SDL2/SDL.h>
    
    #pragma comment(lib,"SDL2.lib")
    #pragma comment(lib,"SDL2main.lib")
    #pragma comment(lib,"opengl32.lib")
    #pragma comment(lib,"glu32.lib")
    #pragma comment(lib,"glew32.lib")
    
    namespace sample1 {
    class Effect {
    public:
      typedef std::shared_ptr<Effect> Ptr;
      Effect(const std::string& vertex_shader_file, const std::string& fragment_shader_file) {
        Init(vertex_shader_file,fragment_shader_file);
      }
      ~Effect() {
        //各シェーダの利用を終了
        glDetachShader(shader_id_, vertex_shader_id_);
        glDetachShader(shader_id_, fragment_shader_id_);
        //シェーダの破棄
        glDeleteShader(vertex_shader_id_);
        glDeleteShader(fragment_shader_id_);
        //シェーダプログラムの削除
        glDeleteProgram(shader_id_);
      }
    
      //シェーダの利用を開始する
      void Begin() {
        glUseProgram(shader_id_);
      }
    
      //シェーダの利用を終了する
      void End() {
        glUseProgram(0);
      }
    
      //プログラムIDの取得
      GLuint shader_id()const {return shader_id_;}
    private:
      void Init(const std::string& vertex_shader_file, const std::string& fragment_shader_file) {
        if (vertex_shader_file.empty() || fragment_shader_file.empty()) {
          return;
        }
        //頂点シェーダの生成
        vertex_shader_id_ = glCreateShader(GL_VERTEX_SHADER);
        //フラグメントシェーダの生成
        fragment_shader_id_ = glCreateShader(GL_FRAGMENT_SHADER);
        //ソースコードの読み込み
        std::string vertex_source = ReadFile(vertex_shader_file);
        std::string fragment_source = ReadFile(fragment_shader_file);
        const char* v_source = vertex_source.c_str();
        const char* f_source = fragment_source.c_str();
        if (vertex_source.empty() || fragment_source.empty()) {
          return;
        }
        //シェーダとソースをバインド
        glShaderSource(vertex_shader_id_, 1, &v_source, 0);
        glShaderSource(fragment_shader_id_, 1, &f_source, 0);
        //シェーダをコンパイル
        glCompileShader(vertex_shader_id_);
        glCompileShader(fragment_shader_id_);
        //シェーダプログラムの生成
        shader_id_ = glCreateProgram();
        //利用するシェーダを登録
        glAttachShader(shader_id_, vertex_shader_id_);
        glAttachShader(shader_id_, fragment_shader_id_);
        //各シェーダの接続
        glLinkProgram(shader_id_);
      }
      std::string ReadFile(const std::string& filename) {
        std::ifstream ifs(filename);
        if(ifs.is_open())return nullptr;
        std::istreambuf_iterator ifs_begin(ifs);
        std::istreambuf_iterator ifs_end;
        std::string file_string(ifs_begin,ifs_end);
        return file_string;
      }
    
      GLuint shader_id_;
      GLuint vertex_shader_id_;
      GLuint fragment_shader_id_;
    };
    
    class Sample1 {
    public:
      Sample1() {
        window_ = nullptr;
      }
      ~Sample1() {
        UnInit();
      }
    
      bool Run() {
        if (!Init()) return false;
        if (!InitRenderObjects()) return false;
        while (PollEvent()) {
          Render();
        }
        return true;
      }
    
    private:
      bool Init() {
        //SDLの初期化
        if (SDL_Init(SDL_INIT_VIDEO) < 0) {
          std::cerr << "Init video failed.\n";
          return false;
        }
      
        //利用するOpenGLのバージョンを設定
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
        //深度バッファの設定
        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
        //ダブルバッファの設定
       SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    
        //ウィンドウの生成
        const int WINDOW_X      = 100;
        const int WINDOW_Y      = 100;
        const int WINDOW_WIDTH  = 800;
        const int WINDOW_HEIGHT = 600;
        window_ = SDL_CreateWindow(
          "sample",
          WINDOW_X,
          WINDOW_Y,
          WINDOW_WIDTH,
          WINDOW_HEIGHT,
          SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
    
        if(window_ == nullptr) {
          std::cerr << "CreateWindow failed.\n";
          return false;
        }
    
        //OpenGLコンテキストの生成
        gl_context_ = SDL_GL_CreateContext(window_);
        
        //描画を同期
        SDL_GL_SetSwapInterval(1);
        
        //GLEW(OpenGL拡張)の初期化
        if (glewInit() != GLEW_OK) {
          std::cout << "Init GLEW failed.\n";
          return false;
        }
        return true;
      }
    
      void UnInit() {
        SDL_Quit();
      }
    
      bool PollEvent() {
        SDL_Event ev;
        while (SDL_PollEvent(&ev)) {
          if (ev.type == SDL_QUIT) return false;
          if (ev.type == SDL_KEYDOWN &&
              ev.key.keysym.sym == SDLK_F4 &&
              (ev.key.keysym.mod & KMOD_ALT) != 0) {
            return false;
          }
        }
        return true;
      }
    
      bool InitRenderObjects() {
        shader_ = Effect::Ptr(new Effect("shader.vs","shader.ps"));
        return true;
      }
    
      void Render() {
        //画面の消去
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //シェーダの利用を開始する
        glUseProgram(shader_id_);
    
        //ここに描画処理
    
        //シェーダの利用を終了する
        glUseProgram(0);
        //画面に描画する
        SDL_GL_SwapWindow(window_);
      }
    
      SDL_Window* window_;
      SDL_GLContext gl_context_;
      Effect::Ptr shader_;
    };
    
    }
    int main(int argc, char* argv[]) {
      sample1::Sample1 sample;
      sample.Run();
      return 0;
    }
    

    SDLでOpenGL4.2

    必要環境
    • SDL2 - OpenGLの拡張に対応している
    • GLEW - OpenGLの拡張部分のエントリポイント(関数群)を記述している

    最初に注意点ですが、 OpenGL4.2ではglBegin(),glEnd(),glVertex**が使えません。
    環境によっては使えるようですが、非推奨のようです。
    描画には、VAO(Vertex Array Object)やVBO(Vertex Buffer Object)が必要になります。
    また、VBOの表示には最低1つのVAOが必要になるようです。

    1. 初期化
        SDL_Window* window_;
        SDL_GLContext gl_context_;
        Effect::Ptr shader_;
        bool Init() {
          //SDLの初期化
          if (SDL_Init(SDL_INIT_VIDEO) < 0) {
            std::cerr << "Init video failed.\n";
            return false;
          }
        
          //利用するOpenGLのバージョンを設定
          SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
          SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
          //深度バッファの設定
          SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
          //ダブルバッファの設定
          SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
      
          //ウィンドウの生成
          const int WINDOW_X      = 100;
          const int WINDOW_Y      = 100;
          const int WINDOW_WIDTH  = 800;
          const int WINDOW_HEIGHT = 600;
          window_ = SDL_CreateWindow(
            "sample",
            WINDOW_X,
            WINDOW_Y,
            WINDOW_WIDTH,
            WINDOW_HEIGHT,
            SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
      
          if(window_ == nullptr) {
            std::cerr << "CreateWindow failed.\n";
            return false;
          }
      
          //OpenGLコンテキストの生成
          gl_context_ = SDL_GL_CreateContext(window_);
          
          //描画を同期
          SDL_GL_SetSwapInterval(1);
          
          //GLEW(OpenGL拡張)の初期化
          if (glewInit() != GLEW_OK) {
            std::cout << "Init GLEW failed.\n";
            return false;
          }
          return true;
        }
      
      ここでは、SDLの初期化、ウィンドウの生成、GLEWの初期化を行なっています。
      SDL_Init(SDL_INIT_VIDEO)は、SDLの初期化です。
      SDL_GL_SetAttribute(,)では、SDLからOpenGLの初期化に必要な設定をしています。
      SDL_CreateWindow(...)で、ウィンドウを生成しています。SDL2からは返り値がSDL_Window*に変更されているようです。
      SDL_GL_CreateContext()でウィンドウに対するOpenGLコンテキストを生成しています。 SDL_GL_SetSwapInterval(1)とすると、OpenGLの描画が垂直同期に合わせるようになります。 glewInit()で、GLEWを初期化し、OpenGLの拡張が全て利用できるようになります。
    2. シェーダの準備
        GLuint shader_id_;
        GLuint vertex_shader_id_;
        GLuint fragment_shader_id_;
        void InitShader(const std::string& vertex_shader_file, const std::string& fragment_shader_file) {
          if (vertex_shader_file.empty() || fragment_shader_file.empty()) {
            return;
          }
          //頂点シェーダの生成
          vertex_shader_id_ = glCreateShader(GL_VERTEX_SHADER);
          //フラグメントシェーダの生成
          fragment_shader_id_ = glCreateShader(GL_FRAGMENT_SHADER);
          //ソースコードの読み込み
          std::string vertex_source = ReadFile(vertex_shader_file);
          std::string fragment_source = ReadFile(fragment_shader_file);
          const char* v_source = vertex_source.c_str();
          const char* f_source = fragment_source.c_str();
          if (vertex_source.empty() || fragment_source.empty()) {
            return;
          }
          //シェーダとソースをバインド
          glShaderSource(vertex_shader_id_, 1, &v_source, 0);
          glShaderSource(fragment_shader_id_, 1, &f_source, 0);
          //シェーダをコンパイル
          glCompileShader(vertex_shader_id_);
          glCompileShader(fragment_shader_id_);
          //シェーダプログラムの生成
          shader_id_ = glCreateProgram();
          //利用するシェーダを登録
          glAttachShader(shader_id_, vertex_shader_id_);
          glAttachShader(shader_id_, fragment_shader_id_);
          //各シェーダの接続
          glLinkProgram(shader_id_);
        }
      
      シェーダ初期化の基本的な流れはシェーダの生成→ソースコードの読み込み→コンパイルです。
      そして、生成されたシェーダを「Program」として、リンクします。ここでの「Program」はパイプラインの流れのようなものです。
      glCreateShaderでシェーダの生成をします。引数で、シェーダの種類を指定しています。
      GL_VERTEX_SHADER​頂点シェーダ
      GL_TESS_CONTROL_SHADER​
      GL_TESS_EVALUATION_SHADER​
      GL_GEOMETRY_SHADER​ジオメトリシェーダ
      GL_FRAGMENT_SHADER​フラグメントシェーダ(ピクセルシェーダ)
      GL_COMPUTE_SHADER​コンピュートシェーダ
      glShaderSourceでシェーダとソースコードをバインドします。
      glCompileShaderでバインドされたソースコードをコンパイルします。
      「Program」は、どのシェーダを利用するかの流れを格納したものです。HLSLにおけるpassのようなものだと思われます。
      glCreateProgramで「Program」を生成します。
      glAttachShaderで「Program」で利用するシェーダを登録します。
      同種類のシェーダを複数登録することは可能だそうです。 これによりシェーダプログラムの分割ができます。
      glLinkProgramで登録されたシェーダを接続します。
    3. 描画
          //画面の消去
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          //シェーダの利用を開始する
          glUseProgram(shader_id_);
      
          //ここに描画処理
      
          //シェーダの利用を終了する
          glUseProgram(0);
          //画面に描画する
          SDL_GL_SwapWindow(window_);
      
      描画時にglUseProgramを呼び出して、使用するシェーダをしていします。
      オブジェクトの描画については、glBegin,glEndなどのコードが使えない環境では長くなるので、別に記述します。
    4. shader.vs
      void main() {
        /*gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;*/
        /* gl_ModelViewProjectionMatrixとgl_VertexはOpenGL4.2では利用できないようです*/
      }
      
      頂点シェーダプログラムの例です。
      デフォルトの入出力変数を使って、座標変換を行なっています。
      座標変換用の行列も自分で指定する必要があるようです。
    5. shader.ps
      void main() {
        /*gl_FragColor = vec4(1.0);*/
        /*gl_FragColorも利用できないようです*/
      }
      
      フラグメントシェーダの例です。
      全部白にしています。

    サンプルコード

    参考:http://www.opengl.org/wiki/Tutorials