- SDL2 - OpenGLの拡張に対応している
- GLEW - OpenGLの拡張部分のエントリポイント(関数群)を記述している
最初に注意点ですが、 OpenGL4.2ではglBegin(),glEnd(),glVertex**が使えません。
環境によっては使えるようですが、非推奨のようです。
描画には、VAO(Vertex Array Object)やVBO(Vertex Buffer Object)が必要になります。
また、VBOの表示には最低1つのVAOが必要になるようです。
- 初期化
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の拡張が全て利用できるようになります。 - シェーダの準備
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で登録されたシェーダを接続します。
- 描画
//画面の消去 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //シェーダの利用を開始する glUseProgram(shader_id_); //ここに描画処理 //シェーダの利用を終了する glUseProgram(0); //画面に描画する SDL_GL_SwapWindow(window_);描画時にglUseProgramを呼び出して、使用するシェーダをしていします。
オブジェクトの描画については、glBegin,glEndなどのコードが使えない環境では長くなるので、別に記述します。
- shader.vs
void main() { /*gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;*/ /* gl_ModelViewProjectionMatrixとgl_VertexはOpenGL4.2では利用できないようです*/ }頂点シェーダプログラムの例です。
デフォルトの入出力変数を使って、座標変換を行なっています。
座標変換用の行列も自分で指定する必要があるようです。
- shader.ps
void main() { /*gl_FragColor = vec4(1.0);*/ /*gl_FragColorも利用できないようです*/ }フラグメントシェーダの例です。
全部白にしています。
サンプルコード
参考:http://www.opengl.org/wiki/Tutorials
0 件のコメント:
コメントを投稿