前回のサンプルのコードを整理していきます。
プログラムはこちらで公開しています。
https://github.com/matsushima-terunao/opengl_sample
初期化処理
atexit() は標準関数で、プログラム終了時に呼ばれる関数を登録します。登録した関数 atexit_function() 内では、発生したエラー表示と GLFW の終了処理を行っています。
atexit(atexit_function);
GLFW 初期化
GLFW の初期化を行います。
glfwSetErrorCallback() で GLFW のエラーが発生したときに呼ばれる関数を登録します。
OpenGL のバージョンは 4.3 core(Mac は 4.1 core) を指定しています。
// GLFW 初期化
glfwSetErrorCallback(glfw_error_callback); // エラー発生時のコールバック指定
if (GL_FALSE == glfwInit()) {
std::cerr << "!glfwInit()" << std::endl;
return 1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
#ifdef __APPLE__
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
#endif
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // MacOS で必須
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Core Profile
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); // デバッグモード
GLFWwindow* const window = glfwCreateWindow(1280, 720, "sample", NULL, NULL); // ウィンドウ作成
if (nullptr == window) {
std::cerr << "!glfwCreateWindow()" << std::endl;
glfwTerminate();
return 1;
}
glfwSetKeyCallback(window, glfw_key_callback); // キーコールバック指定
glfwMakeContextCurrent(window); // 描画対象
glfwSwapInterval(1); // バッファ切り替え間隔
OpenGL 初期化
gladLoadGLLoader() で、前回 glad を生成したときに指定したバージョンの OpenGL の関数をロードします。
// OpenGL 初期化
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cerr << "!gladLoadGLLoader()" << std::endl;
glfwTerminate();
return 1;
}
// デバッグ出力有効
if (NULL != glDebugMessageCallback) {
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(gl_debug_message_callback, 0);
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // カラーバッファーをクリアする色
glEnable(GL_DEPTH_TEST); // デプステストを有効にする
glDepthFunc(GL_LESS); // 前のものよりもカメラに近ければ、フラグメントを受け入れる
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); // フラットシェーディング
モデル作成
三角形のモデルを作成します。
// モデル作成。
create_triangle_model(triangle_model);
/**
* triangle
*/
static void create_triangle_model(Model& model) {
static float vertsf[] = {
-0.6f, -0.4f, 1.f, 0.f, 0.f ,
0.6f, -0.4f, 0.f, 1.f, 0.f ,
0.f, 0.6f, 0.f, 0.f, 1.f
};
model.vertsf = vertsf;
model.verts_count = sizeof(vertsf) / sizeof(Vertex);
model.verts_stride = sizeof(Vertex);
model.polys = nullptr;
model.polys_count = 0;
create_vertex_buffer(model.vertex_array, model.vertex_buffer, model.element_buffer,
sizeof(vertsf), vertsf, sizeof(Vertex), 0, nullptr);
}
VAO, VBO, EBO を作成するラッパー関数です。
/**
* メッシュ作成。
*/
static void create_vertex_buffer(
GLuint& array_buffer, GLuint& vertex_buffer, GLuint& element_buffer,
GLsizeiptr size, const void* data, GLsizei stride, GLsizeiptr element_size, const void* element_data
) {
std::cout << "< create_vertex_buffer(): size = " << size << ", element_size = " << element_size << std::endl;
// VAO(vertex array object) 作成
glGenVertexArrays(1, &array_buffer);
assert(0 != array_buffer && "create_vertex_buffer(): glGenVertexArrays(1, &array_buffer);");
glBindVertexArray(array_buffer);
// VBO(vertex buffer object) 作成
glGenBuffers(1, &vertex_buffer);
assert(0 != vertex_buffer && "create_vertex_buffer(): glGenBuffers(1, &vertex_buffer);");
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
glEnableVertexAttribArray(position_location);
glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(color_location);
glVertexAttribPointer(color_location, 3, GL_FLOAT, GL_FALSE, stride, (void*)(2 * sizeof(GLfloat)));
// EBO(element array buffer object) 作成
if (nullptr != element_data) {
glGenBuffers(1, &element_buffer);
assert(0 != element_buffer && "create_vertex_buffer(): glGenBuffers(1, &element_buffer);");
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, element_size, element_data, GL_STATIC_DRAW);
}
}
シェーダー作成
シェーダーを作成し、描画に使用するシェーダーとして指定します。
// シェーダー作成。
const GLuint program = glCreateProgram();
const GLchar* shader_src[] = { vertex_shader_src.c_str(), fragment_shader_src.c_str() };
create_shader("vertex shader", program, GL_VERTEX_SHADER, shader_src);
create_shader("fragment shader", program, GL_FRAGMENT_SHADER, shader_src + 1);
glLinkProgram(program);
glUseProgram(program);
/**
* シェーダー作成。
*/
static GLuint create_shader(const char* name, const GLuint program, GLenum shaderType, const GLchar** string) {
// シェーダ―作成
const GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, string, NULL);
glCompileShader(shader);
// コンパイル結果
GLint compile_status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
if (GL_FALSE == compile_status) {
std::cerr << "create_shader(): !glCompileShader(): " << name << std::endl;
}
GLsizei maxLength, length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
if (maxLength > 1) {
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(shader, maxLength, &length, infoLog.data());
std::cerr << "create_shader(): glGetShaderInfoLog(): " << name << std::endl;
std::cerr << infoLog.data() << std::endl;
}
assert(GL_FALSE != compile_status && "create_shader(): !glCompileShader()");
if (GL_FALSE == compile_status) {
exit(1);
}
// プログラムオブジェクトにアタッチ
glAttachShader(program, shader);
glDeleteShader(shader);
return shader;
}
/** バーテックスシェーダーのソースプログラム。 */
static const std::string vertex_shader_src = R"(
#version 410 core
/**
* 頂点情報 uniform 構造体定義、現在データ。
* @see struct vertex_uniform
*/
layout(std140) uniform vertex_uniform {
mat4 modelview_projection_matrix;
};
layout (location = 0) in vec3 position; // x, y, z: 頂点座標
layout (location = 1) in vec3 color; // r, g, b: 頂点カラー
out vec4 vertex_color; // 頂点カラー
void main() {
gl_Position = modelview_projection_matrix * vec4(position, 1.0); // 頂点座標
vertex_color = vec4(color, 1.0);
}
)";
/** フラグメントシェーダーのソースプログラム。 */
static const std::string fragment_shader_src = R"(
#version 410 core
in vec4 vertex_color; // 頂点カラー
out vec4 fragment_color; // 出力ピクセルカラー
void main() {
// 頂点カラー
fragment_color = vertex_color;
}
)";
エラー判定
ここまでの初期化処理で、OpenGL のエラーが発生していたらプログラムを終了します。
// エラー判定
GLenum error = GL_GET_ERRORS();
assert(GL_NO_ERROR == error && "main: glGetError();");
if (GL_NO_ERROR != error) {
exit(1);
}
メインループ
ウィンドウが閉じられるまでループ処理を行います。ループ内ではまず描画領域の準備をおこないます。
// メインループ
while (GL_FALSE == glfwWindowShouldClose(window)) {
double time = glfwGetTime();
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
パラメーター計算
モデルの座標計算を行います。
// パラメーター計算。
float ratio = (float)width / height;
calc_params((float)time, ratio);
/**
* パラメーター計算。
*/
static void calc_params(float time, float ratio) {
// パラメーター変更
// ビューパラメーター
ortho.left = -ratio;
ortho.right = ratio;
ortho.bottom = -1.0;
ortho.top = 1.0;
// モデルパラメーター
triangle_model.a += triangle_model.va;
triangle_model.b += triangle_model.vb;
triangle_model.c += triangle_model.vc;
// 変換
mat4x4 model_matrix, view_matrix, projection_matrix;
// モデル変換
mat4x4_identity(model_matrix);
mat4x4_translate(model_matrix, triangle_model.x, triangle_model.y, triangle_model.z);
mat4x4_rotate_X(model_matrix, model_matrix, triangle_model.a);
mat4x4_rotate_Y(model_matrix, model_matrix, triangle_model.b);
mat4x4_rotate_Z(model_matrix, model_matrix, triangle_model.c);
// ビュー変換
mat4x4_look_at(view_matrix, camera_eye, camera_center, camera_up);
// プロジェクション変換
mat4x4_ortho(projection_matrix, ortho.left, ortho.right, ortho.bottom, ortho.top, ortho.near, ortho.far);
// MVP
mat4x4_mul(vertex_uniform.modelview_projection_matrix, projection_matrix, view_matrix);
mat4x4_mul(vertex_uniform.modelview_projection_matrix, vertex_uniform.modelview_projection_matrix, model_matrix);
}
シェーダー設定
計算した座標を uniform buffer へ転送し、シェーダーに反映させます。
// シェーダー設定
glUseProgram(program);
glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer);
GLvoid* buf = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(vertex_uniform), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(buf, &vertex_uniform, sizeof(vertex_uniform));
glUnmapBuffer(GL_UNIFORM_BUFFER);
モデル描画
モデルの描画を行い、バッファを切り替えて画面に反映します。
// モデル描画
// 描画
glBindVertexArray(triangle_model.vertex_array);
if (0 == triangle_model.element_buffer) {
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangle_model.verts_count);
}
else {
glDrawElements(GL_TRIANGLES, (GLsizei)triangle_model.polys_count, GL_UNSIGNED_INT, NULL);
}
// 描画反映
glfwSwapBuffers(window);
glfwPollEvents();
エラー処理
GLFW のエラー処理
GLFW のエラーが発生したときに呼ばれるコールバック関数を登録します。エラー発生時にはエラー情報を表示します。
// GLFW 初期化
glfwSetErrorCallback(glfw_error_callback); // エラー発生時のコールバック指定
/**
* GLFW エラーのコールバック。
glfw_error_callback(): 65543: WGL: Driver does not support OpenGL version 5.3
Assertion failed: 0 && "glfw_error_callback()", file C:\USR\src\blog\opengl_sample\vs\opengl_sample\opengl_sample\sample02.cpp, line 159
*/
static void glfw_error_callback(int error, const char* description) {
std::cerr << "glfw_error_callback(): " << error << ": " << description << std::endl;
assert(0 && "glfw_error_callback()");
}
OpenGL のエラー処理
初期化終了時とプログラム終了時にエラー判定し、エラーがあった場合はエラー表示して終了します。
// エラー判定
GLenum error = GL_GET_ERRORS();
assert(GL_NO_ERROR == error && "main: glGetError();");
if (GL_NO_ERROR != error) {
exit(1);
}
atexit(atexit_function);
/**
* 終了ハンドラ。
*/
static void atexit_function() {
GLenum error = GL_GET_ERRORS();
assert(GL_NO_ERROR == error && "atexit_function(): glGetError();");
glfwTerminate();
}
/**
* GLエラー出力。
*/
static GLenum gl_get_errors(const char* file, int line, const char* msg = "") {
if (NULL == glGetError) {
std::cerr << file << "(" << line << "): " << "gl_get_errors(): NULL == glGetError" << msg << std::endl;
return 1;
}
GLenum error, first_error = GL_NO_ERROR;
while (GL_NO_ERROR != (error = glGetError())) {
if (GL_NO_ERROR == first_error) {
first_error = error;
}
std::cerr << file << "(" << line << "): " << "gl_get_errors(): glGetError() = " << msg << error << std::endl;
}
return first_error;
}
#define GL_GET_ERRORS(...) gl_get_errors(__FILE__, __LINE__ __VA_ARGS__)
コメント