OpenGLサンプル5 – テキスト描画

OpenGL

画像ファイルを読み込んでテクスチャーを表示するように修正します。

プログラムはこちらで公開しています。
https://github.com/matsushima-terunao/opengl_sample

参考サイト
https://learnopengl.com/In-Practice/Text-Rendering

FreeType の環境設定

こちらで FreeType の環境設定を行ってください。
https://mappuri.com/program/freetype-settings/

テキスト描画準備

文字イメージセット用のテクスチャーを用意し、各文字のイメージを書き込みます。また、各文字の座標を取得します。
また、テキスト描画用のバッファ作成します。

/**
 * テキスト描画初期化。
 */
int init_render_text() {
	// 文字イメージセット描画用テクスチャー準備
	glEnable(GL_CULL_FACE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	// シェーダー作成。
	program = glCreateProgram();
	create_shader("text vertex shader", program, GL_VERTEX_SHADER, &text_vertex_shader);
	create_shader("text fragment shader", program, GL_FRAGMENT_SHADER, &text_fragment_shader);
	glLinkProgram(program);
	glUseProgram(program);
	// 透視設定
	mat4x4 projection;
	mat4x4_ortho(projection, 0.0f, static_cast<float>(SCR_WIDTH), 0.0f, static_cast<float>(SCR_HEIGHT), 1.f, -100.f);
	glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, (const GLfloat*)projection);
	// テクスチャー作成
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, ch_width * ch_count, ch_width, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
	// FreeType 初期化
	FT_Error ft_error;
	ft_error = FT_Init_FreeType(&ft);
	assert(!ft_error && "init_resources: FT_Init_FreeType();");
	std::string font_name = "assets/font/FreeSans.ttf";
	ft_error = FT_New_Face(ft, font_name.c_str(), 0, &face);
	assert(!ft_error && "init_resources: FT_New_Face();");
	FT_Set_Pixel_Sizes(face, 0, ch_width);

	// 文字ごとのイメージ書き込み
	for (unsigned char c = 0; c < ch_count; ++c) {
		ft_error = FT_Load_Char(face, c, FT_LOAD_RENDER);
		if (ft_error) {
			assert(!ft_error && "init_resources: FT_LOAD_RENDER();");
			continue;
		}
		// 文字イメージをテクスチャーに書き込み
		if (face->glyph->bitmap.width > 0) {
			glTexSubImage2D(GL_TEXTURE_2D, 0, c * ch_width, 0, face->glyph->bitmap.width, face->glyph->bitmap.rows,
				GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
		}
		// 文字座標取得
		character_textures[c] = {
			face->glyph->bitmap_left, face->glyph->bitmap_top,
			(int)face->glyph->bitmap.width, (int)face->glyph->bitmap.rows,
			(int)face->glyph->advance.x,
		};
	}

	// 文字イメージセット描画終了
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glBindTexture(GL_TEXTURE_2D, 0);
	FT_Done_Face(face);
	FT_Done_FreeType(ft);

	// テキスト描画用バッファ作成
	glGenVertexArrays(1, &vertex_array);
	glGenBuffers(1, &vertex_object);
	glBindVertexArray(vertex_array);
	glBindBuffer(GL_ARRAY_BUFFER, vertex_object);
	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4 * str_length, NULL, GL_DYNAMIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	return 0;
}

テキスト描画

文字イメージセットが描画されたテクスチャーから、各文字のイメージを切り出したポリゴンを作成します。

/**
 * テキスト描画。
 */
float render_text(const char* text, float x, float y, float scale, float color[3], bool proportional) {
	// テキスト描画用バッファ準備
	glUseProgram(program);
	glUniform3f(glGetUniformLocation(program, "texture_color"), color[0], color[1], color[2]);
	glActiveTexture(GL_TEXTURE0);
	glBindVertexArray(vertex_array);

	// テキストポリゴン配列準備
	char* vertices_buf = new char[strlen(text) * 4 * 24];
	int i;
	for (i = 0; i < str_length && '\0' != text[i]; ++i) {
		// テクスチャー切り出し
		character_rectangle_info& ch = character_textures[text[i]];
		float xpos = x + ch.x * scale;
		float ypos = y - (ch.h - ch.y) * scale;
		float w = ch.w * scale;
		float h = ch.h * scale * 1.5f;
		float u = 1.0f / ch_count * text[i];
		float uw = (float)ch.w / ch_width / ch_count;
		float uh = (float)ch.h / ch_width;
		if (!proportional) {
			xpos += (ch_width * scale - w) / 2;
		}
		float vertices[6][4] = {
			{ xpos,     ypos + h, u,      0.0f }, // 0
			{ xpos,     ypos,     u,      uh },
			{ xpos + w, ypos,     u + uw, uh },
			{ xpos,     ypos + h, u,      0.0f }, // 1
			{ xpos + w, ypos,     u + uw, uh },
			{ xpos + w, ypos + h, u + uw, 0.0f },
		};

		// ポリゴン追加
		memcpy(vertices_buf + i * sizeof(vertices), vertices, sizeof(vertices));
		x += proportional ? (ch.advance_x >> 6) * scale : ch_width * scale / 1.5f;
	}

	// ポリゴン描画
	glBindTexture(GL_TEXTURE_2D, texture);
	glBindBuffer(GL_ARRAY_BUFFER, vertex_object);
	glBufferSubData(GL_ARRAY_BUFFER, 0, i * 6ull * 4 * sizeof(float), vertices_buf); // be sure to use glBufferSubData and not glBufferData
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glDrawArrays(GL_TRIANGLES, 0, 6 * i);
	glBindVertexArray(0);
	glBindTexture(GL_TEXTURE_2D, 0);
	delete[] vertices_buf;
	return x;
}

パネル表示

現在の状態と設定変更ができるパネルを表示します。
1,2行目は FPS と1フレーム当たりの秒数で、それぞれ平均、平均(CPU 使用率換算)、経過、直近1フレーム、直近1フレーム(CPU 使用率換算)です。
3行目以降は設定表示で、選択パラメーターは上下左右でカーソル移動し、Enter キーで決定です。数値パラメーターは Enter で赤色にしてから上下左右で数値変更し、再度 Enter キーで決定です。

コメント

タイトルとURLをコピーしました