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を有効化することです。


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