はじめに
OpenGLとC言語を使って初音ミクの3Dモデルを表示するプログラムを作成に挑戦しています。今回は3Dモデルの陰影付けとテクスチャマッピングを行います。
過去の記事
- 今どきのOpenGLとC言語で初音ミクを表示する(その1): ウインドウの表示
- 今どきのOpenGLとC言語で初音ミクを表示する(その2): プリミティブの描画
- 今どきのOpenGLとC言語で初音ミクを表示する(その3): 3Dモデルの読み込みと透視投影
陰影を付ける
陰影をつけるために、シェーダを拡張します。以下は空間上に平行光源を配置して、ランバートの余弦則に従って拡散反射を計算するシェーダです。
#version 450
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec3 in_normal;
uniform mat4 mv_matrix;
uniform mat3 normal_matrix;
uniform mat4 mvp;
out vec3 color;
void main()
{
vec3 n = normalize(normal_matrix * in_normal);
vec4 camera = mv_matrix * vec4(in_position, 1.0);
vec4 light = vec4(4.0, 4.0, 4.0, 1.0);
vec3 s = normalize(vec3(light - camera));
color = vec3(1.0, 1.0, 1.0) * max(dot(s, n), 0.0);
gl_Position = mvp * vec4(in_position, 1.0);
}
#version 450
in vec3 color;
out vec4 out_color;
void main()
{
out_color = vec4(color, 1.0);
}
このシェーダは、頂点位置、頂点の法線、モデルビュー行列、正規化行列、MVP行列を受け取る。これらのうち頂点の法線はOBJファイルから読み込むことにします。MetasequoiaではOBJファイルを保存するときに以下のように設定することで、法線を出力できます。
この設定で立方体を出力すると以下のようなOBJファイルが得られます。
# Created by Metasequoia
v -0.500000 0.500000 0.500000
v -0.500000 -0.500000 0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 0.500000
v 0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v -0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
# 8 vertices
vn 0.00000 0.00000 1.00000
vn 1.00000 0.00000 0.00000
vn 0.00000 0.00000 -1.00000
vn -1.00000 0.00000 0.00000
vn 0.00000 1.00000 0.00000
vn 0.00000 -1.00000 0.00000
# 6 normal vertices
f 4//1 3//1 1//1
f 2//1 4//1 1//1
f 6//2 5//2 3//2
f 4//2 6//2 3//2
f 8//3 7//3 5//3
f 6//3 8//3 5//3
f 2//4 1//4 7//4
f 8//4 2//4 7//4
f 3//5 5//5 7//5
f 1//5 3//5 7//5
f 6//6 4//6 2//6
f 8//6 6//6 2//6
# 12 elements
以下はObjLoader.h
とObjLoader.c
を、上のようなOBJファイルを読み込めるように拡張したものです。
#ifndef OBJLOADER_H_INCLUDE
#define OBJLOADER_H_INCLUDE
/* モデルデータ */
typedef struct {
float *vertices;
int num_vertices;
float *normals;
int num_normals;
} model;
/* OBJファイルの読み込み */
void load_obj(model *m, const char *file_name);
#endif
#include <stdio.h>
#include <stdlib.h>
#include "ObjLoader.h"
/* 初期バッファサイズ */
#define DEF_BUF_SIZE 2
/* 浮動小数点数バッファ */
typedef struct {
int buf_size;
int current_index;
float *buf;
} float_buffer;
/* 整数バッファ */
typedef struct {
int buf_size;
int current_index;
int *buf;
} int_buffer;
/* 頂点座標の読み込み */
void read_vertices(const char *line, float_buffer *vs);
/* 法線ベクトルの読み込み */
void read_normals(const char *line, float_buffer *vns);
/* インデックスの読み込み */
void read_indices(const char *line, int_buffer *fs);
/* モデルの作成 */
void create_model(model *m,
float_buffer *vs, float_buffer *vns, int_buffer *fs);
/* 浮動小数点数バッファの操作 */
float_buffer *alloc_float_buffer(void);
void free_float_buffer(float_buffer *fbuf);
void add_float(float_buffer *fbuf, float value);
float get_float(float_buffer *fbuf, int index);
/* 整数バッファの操作 */
int_buffer *alloc_int_buffer(void);
void free_int_buffer(int_buffer *ibuf);
void add_int(int_buffer *ibuf, int value);
int get_int(int_buffer *ibuf, int index);
/*==============================*
** OBJファイルの読み込み
**==============================*/
void load_obj(model *m, const char *file_name)
{
FILE *fp;
char line[1024];
float_buffer *vs, *vns;
int_buffer *fs;
fp = fopen(file_name, "r");
if (!fp) {
fprintf(stderr, "Cannot open %s.\n", file_name);
exit(EXIT_FAILURE);
}
vs = alloc_float_buffer();
vns = alloc_float_buffer();
fs = alloc_int_buffer();
while (!feof(fp)) {
fgets(line, sizeof(line), fp);
if (line[0] == 'v' && line[1] == ' ') {
read_vertices(line, vs);
}
else if (line[0] == 'v' && line[1] == 'n' && line[2] == ' ') {
read_normals(line, vns);
}
else if (line[0] == 'f' && line[1] == ' ') {
read_indices(line, fs);
}
}
create_model(m, vs, vns, fs);
free_float_buffer(vs);
free_float_buffer(vns);
free_int_buffer(fs);
fclose(fp);
}
/*------------------------------*
** 頂点座標の読み込み
**------------------------------*/
void read_vertices(const char *line, float_buffer *vs)
{
float x, y, z;
int count;
count = sscanf(line, "%*s%f%f%f", &x, &y, &z);
if (count == 3) {
add_float(vs, x);
add_float(vs, y);
add_float(vs, z);
}
}
/*------------------------------*
** 法線ベクトルの読み込み
**------------------------------*/
void read_normals(const char *line, float_buffer *vns)
{
float x, y, z;
int count;
count = sscanf(line, "%*s%f%f%f", &x, &y, &z);
if (count == 3) {
add_float(vns, x);
add_float(vns, y);
add_float(vns, z);
}
}
/*------------------------------*
** インデックスの読み込み
**------------------------------*/
void read_indices(const char *line, int_buffer *fs)
{
int v1, v2, v3;
int n1, n2, n3;
int count;
count = sscanf(line,
"%*s %d%*[/]%d %d%*[/]%d %d%*[/]%d",
&v1, &n1, &v2, &n2, &v3, &n3);
if (count == 6) {
add_int(fs, v1);
add_int(fs, n1);
add_int(fs, v2);
add_int(fs, n2);
add_int(fs, v3);
add_int(fs, n3);
}
}
/*------------------------------*
** モデルの作成
**------------------------------*/
void create_model(model *m,
float_buffer *vs, float_buffer *vns, int_buffer *fs)
{
int i, j;
m->num_vertices = fs->current_index / 2 * 3;
m->vertices = malloc(sizeof(float) * m->num_vertices);
if (!m->vertices) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
m->num_normals = fs->current_index / 2 * 3;
m->normals = malloc(sizeof(float) * m->num_normals);
if (!m->normals) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < fs->current_index; i++) {
int idx = fs->buf[i] - 1;
if (i % 2 == 0) {
for (j = 0; j < 3; j++) {
m->vertices[i / 2 * 3 + j] = vs->buf[idx * 3 + j];
}
}
else {
for (j = 0; j < 3; j++) {
m->normals[i / 2 * 3 + j] = vns->buf[idx * 3 + j];
}
}
}
}
/*------------------------------*
** 浮動小数点数バッファの割り当て
**------------------------------*/
float_buffer *alloc_float_buffer(void)
{
float_buffer *fbuf;
fbuf = malloc(sizeof(float_buffer));
if (!fbuf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
fbuf->buf_size = DEF_BUF_SIZE;
fbuf->current_index = 0;
fbuf->buf = malloc(sizeof(float) * fbuf->buf_size);
return fbuf;
}
/*------------------------------*
** 浮動小数点数バッファの解放
**------------------------------*/
void free_float_buffer(float_buffer *fbuf)
{
free(fbuf->buf);
free(fbuf);
}
/*------------------------------*
** バッファに浮動小数点数を追加
**------------------------------*/
void add_float(float_buffer *fbuf, float value)
{
fbuf->buf[fbuf->current_index] = value;
fbuf->current_index++;
if (fbuf->current_index >= fbuf->buf_size) {
fbuf->buf_size *= 2;
fbuf->buf = realloc(fbuf->buf, sizeof(float) * fbuf->buf_size);
if (!fbuf->buf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
}
}
/*------------------------------*
** バッファから浮動小数点数を取り出し
**------------------------------*/
float get_float(float_buffer *fbuf, int index)
{
return fbuf->buf[index];
}
/*------------------------------*
** 整数バッファの割り当て
**------------------------------*/
int_buffer *alloc_int_buffer(void)
{
int_buffer *ibuf;
ibuf = malloc(sizeof(int_buffer));
if (!ibuf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
ibuf->buf_size = DEF_BUF_SIZE;
ibuf->current_index = 0;
ibuf->buf = malloc(sizeof(int) * ibuf->buf_size);
return ibuf;
}
/*------------------------------*
** 整数バッファの解放
**------------------------------*/
void free_int_buffer(int_buffer *ibuf)
{
free(ibuf->buf);
free(ibuf);
}
/*------------------------------*
** バッファに整数を追加
**------------------------------*/
void add_int(int_buffer *ibuf, int value)
{
ibuf->buf[ibuf->current_index] = value;
ibuf->current_index++;
if (ibuf->current_index >= ibuf->buf_size) {
ibuf->buf_size *= 2;
ibuf->buf = realloc(ibuf->buf, sizeof(int) * ibuf->buf_size);
if (!ibuf->buf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
}
}
/*------------------------------*
** バッファから整数を取り出し
**------------------------------*/
int get_int(int_buffer *ibuf, int index)
{
return ibuf->buf[index];
}
Main.c
は以下のように変更します。
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "Init.h"
#include "ShaderLoader.h"
#include "Matrix.h"
#include "ObjLoader.h"
int main(void)
{
GLFWwindow *window;
GLuint program;
model model;
GLuint mv_matrix, normal_matrix, mvp;
int i, j;
float *mat_proj, *mat_view, *mat_model;
float *mat_mv, *mat_mvp;
float mat_norm[9];
GLuint position_buffer;
GLuint normal_buffer;
GLuint vertex_array;
const GLuint position_location = 0;
const GLuint position_bindindex = 0;
const GLuint normal_location = 1;
const GLuint normal_bindindex = 1;
window = init(640, 480, "Step 04");
program = load_shader("Vertex.glsl", "Fragment.glsl");
load_obj(&model, "cube.obj");
mat_proj = alloc_matrix();
set_perspective_matrix(mat_proj,
45.0, 640.0f / 480.0f, 0.1f, 100.0f);
mat_view = alloc_matrix();
set_lookat_matrix(mat_view,
2.0f, 3.0f, 3.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
mat_model = alloc_matrix();
set_identity_matrix(mat_model);
mat_mv = alloc_matrix();
multiply_matrix(mat_mv, mat_view, mat_model);
mat_mvp = alloc_matrix();
multiply_matrix(mat_mvp, mat_proj, mat_mv);
free_matrix(mat_proj);
free_matrix(mat_view);
free_matrix(mat_model);
mv_matrix = glGetUniformLocation(program, "mv_matrix");
glUniformMatrix4fv(mv_matrix, 1, GL_FALSE, mat_mv);
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
mat_norm[i * 3 + j] = mat_mv[i * 4 + j];
}
}
normal_matrix = glGetUniformLocation(program, "normal_matrix");
glUniformMatrix3fv(normal_matrix, 1, GL_FALSE, mat_norm);
mvp = glGetUniformLocation(program, "mvp");
glUniformMatrix4fv(mvp, 1, GL_FALSE, mat_mvp);
glCreateBuffers(1, &position_buffer);
glNamedBufferData(position_buffer,
sizeof(float) * model.num_vertices,
model.vertices,
GL_STATIC_DRAW);
glCreateBuffers(1, &normal_buffer);
glNamedBufferData(normal_buffer,
sizeof(float) * model.num_normals,
model.normals,
GL_STATIC_DRAW);
glCreateVertexArrays(1, &vertex_array);
glEnableVertexArrayAttrib(
vertex_array, position_location);
glVertexArrayAttribFormat(
vertex_array, position_location,
3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(
vertex_array, position_location,
position_bindindex);
glVertexArrayVertexBuffer(
vertex_array, position_bindindex,
position_buffer, 0, sizeof(GLfloat) * 3);
glEnableVertexArrayAttrib(
vertex_array, normal_location);
glVertexArrayAttribFormat(
vertex_array, normal_location,
3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(
vertex_array, normal_location,
normal_bindindex);
glVertexArrayVertexBuffer(
vertex_array, normal_bindindex,
normal_buffer, 0, sizeof(GLfloat) * 3);
glClearColor(0.6, 0.8, 0.8, 1.0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(vertex_array);
glDrawArrays(GL_TRIANGLES, 0, model.num_vertices / 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
free_matrix(mat_mv);
free_matrix(mat_mvp);
glfwTerminate();
return EXIT_SUCCESS;
}
実行すると以下のように表示されます。
以下は球のモデルを表示した場合です。
初音ミクモデルの表示
以下のサイトから、メタセコはちゅね(+ボーンver2.0)nh0217.zip
をダウンロードします。
解凍してMetasequoiaで開きます。
オブジェクトパネルでboneとanchorを削除します。
すべてのオブジェクトを表示して、すべてのオブジェクトを編集可能にします。
すべての頂点を選択して、面を三角形化します。
移動コマンドですべての頂点のY座標を-100します。
以下のフォーマットでOBJファイルを書きだします。
作成中のプログラムで読み込むと以下のように表示されます。
BMP画像の読み込み
テクスチャマッピングをするためにはテクスチャの読み込みを行う必要があります。ここではテクスチャファイルのフォーマットとしてビットマップを採用します。プロジェクトにBmpLoader.h
とBmpLoader.c
を追加して、ビットマップ画像を読み込む関数を以下のように実装します。
#ifndef BMPLOADER_H_INCLUDE
#define BMPLOADER_H_INCLUDE
#include <GL/glew.h>
/* BMPファイルの読み込み */
GLuint load_bmp(const char *file_name);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include "BmpLoader.h"
/*==============================*
** BMPファイルの読み込み
**==============================*/
GLuint load_bmp(const char *file_name)
{
FILE *fp;
unsigned char header[54];
unsigned char *data;
size_t count, size;
int width, height;
GLuint texture;
fp = fopen(file_name, "rb");
if (!fp) {
fprintf(stderr, "Cannot open %s.\n", file_name);
exit(EXIT_FAILURE);
}
count = fread(header, 1, sizeof(header), fp);
if (count != sizeof(header)) {
fprintf(stderr, "Cannot read bmp file %s.\n", file_name);
exit(EXIT_FAILURE);
}
if (header[0] != 'B' || header[1] != 'M') {
fprintf(stderr, "Cannot read bmp file %s.\n", file_name);
exit(EXIT_FAILURE);
}
width = *(int*)(header + 0x12);
height = *(int*)(header + 0x16);
size = width * height * 3;
data = malloc(sizeof(unsigned char) * size);
if (!data) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
count = fread(data, sizeof(unsigned char), size, fp);
if (count != size) {
fprintf(stderr, "Cannot read bmp file %s.\n", file_name);
exit(EXIT_FAILURE);
}
fclose(fp);
glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glTextureStorage2D(texture, 1, GL_RGB8, width, height);
glTextureSubImage2D(texture, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, data);
free(data);
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
return texture;
}
UV座標の読み込み
ObjLoader.h
、ObjLoader.c
を拡張して、UV座標を読み込めるようにします。
#ifndef OBJLOADER_H_INCLUDE
#define OBJLOADER_H_INCLUDE
/* モデルデータ */
typedef struct {
float *vertices;
int num_vertices;
float *normals;
int num_normals;
float *uvs;
int num_uvs;
} model;
/* OBJファイルの読み込み */
void load_obj(model *m, const char *file_name);
#endif
#include <stdio.h>
#include <stdlib.h>
#include "ObjLoader.h"
/* 初期バッファサイズ */
#define DEF_BUF_SIZE 2
/* 浮動小数点数バッファ */
typedef struct {
int buf_size;
int current_index;
float *buf;
} float_buffer;
/* 整数バッファ */
typedef struct {
int buf_size;
int current_index;
int *buf;
} int_buffer;
/* 頂点座標の読み込み */
void read_vertices(const char *line, float_buffer *vs);
/* 法線ベクトルの読み込み */
void read_normals(const char *line, float_buffer *vns);
/* UV座標の読み込み */
void read_uvs(const char *line, float_buffer *vts);
/* インデックスの読み込み */
void read_indices(const char *line, int_buffer *fs);
/* モデルの作成 */
void create_model(model *m,
float_buffer *vs, float_buffer *vns, int_buffer *fs);
/* 浮動小数点数バッファの操作 */
float_buffer *alloc_float_buffer(void);
void free_float_buffer(float_buffer *fbuf);
void add_float(float_buffer *fbuf, float value);
float get_float(float_buffer *fbuf, int index);
/* 整数バッファの操作 */
int_buffer *alloc_int_buffer(void);
void free_int_buffer(int_buffer *ibuf);
void add_int(int_buffer *ibuf, int value);
int get_int(int_buffer *ibuf, int index);
/*==============================*
** OBJファイルの読み込み
**==============================*/
void load_obj(model *m, const char *file_name)
{
FILE *fp;
char line[1024];
float_buffer *vs, *vns, *vts;
int_buffer *fs;
fp = fopen(file_name, "r");
if (!fp) {
fprintf(stderr, "Cannot open %s.\n", file_name);
exit(EXIT_FAILURE);
}
vs = alloc_float_buffer();
vns = alloc_float_buffer();
vts = alloc_float_buffer();
fs = alloc_int_buffer();
while (!feof(fp)) {
fgets(line, sizeof(line), fp);
if (line[0] == 'v' && line[1] == ' ') {
read_vertices(line, vs);
}
else if (line[0] == 'v' && line[1] == 'n' && line[2] == ' ') {
read_normals(line, vns);
}
else if (line[0] == 'v' && line[1] == 't' && line[2] == ' ') {
read_uvs(line, vts);
}
else if (line[0] == 'f' && line[1] == ' ') {
read_indices(line, fs);
}
}
create_model(m, vs, vns, vts, fs);
free_float_buffer(vs);
free_float_buffer(vns);
free_float_buffer(vts);
free_int_buffer(fs);
fclose(fp);
}
/*------------------------------*
** 頂点座標の読み込み
**------------------------------*/
void read_vertices(const char *line, float_buffer *vs)
{
float x, y, z;
int count;
count = sscanf(line, "%*s%f%f%f", &x, &y, &z);
if (count == 3) {
add_float(vs, x);
add_float(vs, y);
add_float(vs, z);
}
}
/*------------------------------*
** 法線ベクトルの読み込み
**------------------------------*/
void read_normals(const char *line, float_buffer *vns)
{
float x, y, z;
int count;
count = sscanf(line, "%*s%f%f%f", &x, &y, &z);
if (count == 3) {
add_float(vns, x);
add_float(vns, y);
add_float(vns, z);
}
}
/*------------------------------*
** UV座標の読み込み
**------------------------------*/
void read_uvs(const char *line, float_buffer *vts)
{
float u, v;
int count;
count = sscanf(line, "%*s%f%f", &u, &v);
if (count == 2) {
add_float(vts, u);
add_float(vts, v);
}
}
/*------------------------------*
** インデックスの読み込み
**------------------------------*/
void read_indices(const char *line, int_buffer *fs)
{
int v1, v2, v3;
int t1, t2, t3;
int n1, n2, n3;
int count;
count = sscanf(line,
"%*s %d%*c%d%*c%d "
"%d%*c%d%*c%d "
"%d%*c%d%*c%d",
&v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);
if (count == 9) {
add_int(fs, v1);
add_int(fs, t1);
add_int(fs, n1);
add_int(fs, v2);
add_int(fs, t2);
add_int(fs, n2);
add_int(fs, v3);
add_int(fs, t3);
add_int(fs, n3);
}
}
/*------------------------------*
** モデルの作成
**------------------------------*/
void create_model(model *m,
float_buffer *vs, float_buffer *vns,
float_buffer *vts, int_buffer *fs)
{
int i, j;
m->num_vertices = fs->current_index / 3 * 3;
m->vertices = malloc(sizeof(float) * m->num_vertices);
if (!m->vertices) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
m->num_normals = fs->current_index / 3 * 3;
m->normals = malloc(sizeof(float) * m->num_normals);
if (!m->normals) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
m->num_uvs = fs->current_index / 3 * 2;
m->uvs = malloc(sizeof(float) * m->num_uvs);
if (!m->uvs) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < fs->current_index; i++) {
int idx = fs->buf[i] - 1;
if (i % 3 == 0) {
for (j = 0; j < 3; j++) {
m->vertices[i / 3 * 3 + j] = vs->buf[idx * 3 + j];
}
}
else if (i % 3 == 1) {
for (j = 0; j < 2; j++) {
m->uvs[i / 3 * 2 + j] = vts->buf[idx * 2 + j];
}
}
else {
for (j = 0; j < 3; j++) {
m->normals[i / 3 * 3 + j] = vns->buf[idx * 3 + j];
}
}
}
}
/*------------------------------*
** 浮動小数点数バッファの割り当て
**------------------------------*/
float_buffer *alloc_float_buffer(void)
{
float_buffer *fbuf;
fbuf = malloc(sizeof(float_buffer));
if (!fbuf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
fbuf->buf_size = DEF_BUF_SIZE;
fbuf->current_index = 0;
fbuf->buf = malloc(sizeof(float) * fbuf->buf_size);
return fbuf;
}
/*------------------------------*
** 浮動小数点数バッファの解放
**------------------------------*/
void free_float_buffer(float_buffer *fbuf)
{
free(fbuf->buf);
free(fbuf);
}
/*------------------------------*
** バッファに浮動小数点数を追加
**------------------------------*/
void add_float(float_buffer *fbuf, float value)
{
fbuf->buf[fbuf->current_index] = value;
fbuf->current_index++;
if (fbuf->current_index >= fbuf->buf_size) {
fbuf->buf_size *= 2;
fbuf->buf = realloc(fbuf->buf, sizeof(float) * fbuf->buf_size);
if (!fbuf->buf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
}
}
/*------------------------------*
** バッファから浮動小数点数を取り出し
**------------------------------*/
float get_float(float_buffer *fbuf, int index)
{
return fbuf->buf[index];
}
/*------------------------------*
** 整数バッファの割り当て
**------------------------------*/
int_buffer *alloc_int_buffer(void)
{
int_buffer *ibuf;
ibuf = malloc(sizeof(int_buffer));
if (!ibuf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
ibuf->buf_size = DEF_BUF_SIZE;
ibuf->current_index = 0;
ibuf->buf = malloc(sizeof(int) * ibuf->buf_size);
return ibuf;
}
/*------------------------------*
** 整数バッファの解放
**------------------------------*/
void free_int_buffer(int_buffer *ibuf)
{
free(ibuf->buf);
free(ibuf);
}
/*------------------------------*
** バッファに整数を追加
**------------------------------*/
void add_int(int_buffer *ibuf, int value)
{
ibuf->buf[ibuf->current_index] = value;
ibuf->current_index++;
if (ibuf->current_index >= ibuf->buf_size) {
ibuf->buf_size *= 2;
ibuf->buf = realloc(ibuf->buf, sizeof(int) * ibuf->buf_size);
if (!ibuf->buf) {
fprintf(stderr, "Memory allocation error.\n");
exit(EXIT_FAILURE);
}
}
}
/*------------------------------*
** バッファから整数を取り出し
**------------------------------*/
int get_int(int_buffer *ibuf, int index)
{
return ibuf->buf[index];
}
テクスチャマッピング
シェーダを以下のように書き替えて、テクスチャマッピングを行うようにします。
#version 450
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec3 in_normal;
layout (location = 2) in vec2 in_uv;
uniform mat4 mv_matrix;
uniform mat3 normal_matrix;
uniform mat4 mvp;
out vec4 position;
out vec3 normal;
out vec2 uv;
void main()
{
uv = in_uv;
normal = normalize(normal_matrix * in_normal);
position = mv_matrix * vec4(in_position, 1.0);
gl_Position = mvp * vec4(in_position, 1.0);
}
#version 450
in vec4 position;
in vec3 normal;
in vec2 uv;
uniform sampler2D texl;
out vec4 out_color;
void main()
{
vec4 light = vec4(0.0, 2.0, 4.0, 1.0);
vec3 s = normalize(vec3(light - position));
vec3 diffusion = vec3(1.0, 1.0, 1.0) * max(dot(s, normal), 0.0);
vec3 material = texture2D(texl, uv).rgb;
out_color = vec4(diffusion * material, 1.0);
}
シェーダに情報を受け渡すようにMain.c
を以下のように書き替えます。
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "Init.h"
#include "ShaderLoader.h"
#include "Matrix.h"
#include "ObjLoader.h"
#include "BmpLoader.h"
int main(void)
{
GLFWwindow *window;
GLuint program;
model model;
GLuint texture, texl;
GLuint mv_matrix, normal_matrix, mvp;
int i, j;
float *mat_proj, *mat_view, *mat_model;
float *mat_mv, *mat_mvp;
float mat_norm[9];
GLuint position_buffer;
GLuint normal_buffer;
GLuint uv_buffer;
GLuint vertex_array;
const GLuint position_location = 0;
const GLuint position_bindindex = 0;
const GLuint normal_location = 1;
const GLuint normal_bindindex = 1;
const GLuint uv_location = 2;
const GLuint uv_bindindex = 2;
window = init(640, 480, "Step 04");
program = load_shader("Vertex.glsl", "Fragment.glsl");
load_obj(&model, "miku.obj");
mat_proj = alloc_matrix();
set_perspective_matrix(mat_proj,
45.0, 640.0f / 480.0f, 0.1f, 100.0f);
mat_view = alloc_matrix();
set_lookat_matrix(mat_view,
1.0f, 1.0f, 3.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
mat_model = alloc_matrix();
set_identity_matrix(mat_model);
mat_mv = alloc_matrix();
multiply_matrix(mat_mv, mat_view, mat_model);
mat_mvp = alloc_matrix();
multiply_matrix(mat_mvp, mat_proj, mat_mv);
free_matrix(mat_proj);
free_matrix(mat_view);
free_matrix(mat_model);
mv_matrix = glGetUniformLocation(program, "mv_matrix");
glUniformMatrix4fv(mv_matrix, 1, GL_FALSE, mat_mv);
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
mat_norm[i * 3 + j] = mat_mv[i * 4 + j];
}
}
normal_matrix = glGetUniformLocation(program, "normal_matrix");
glUniformMatrix3fv(normal_matrix, 1, GL_FALSE, mat_norm);
mvp = glGetUniformLocation(program, "mvp");
glUniformMatrix4fv(mvp, 1, GL_FALSE, mat_mvp);
texture = load_bmp("miku.bmp");
texl = glGetUniformLocation(program, "texl");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(texl, 0);
glCreateBuffers(1, &position_buffer);
glNamedBufferData(position_buffer,
sizeof(float) * model.num_vertices,
model.vertices,
GL_STATIC_DRAW);
glCreateBuffers(1, &normal_buffer);
glNamedBufferData(normal_buffer,
sizeof(float) * model.num_normals,
model.normals,
GL_STATIC_DRAW);
glCreateBuffers(1, &uv_buffer);
glNamedBufferData(uv_buffer,
sizeof(float) * model.num_uvs,
model.uvs,
GL_STATIC_DRAW);
printf("%d %d %d\n", model.num_vertices, model.num_normals, model.num_uvs);
glCreateVertexArrays(1, &vertex_array);
glEnableVertexArrayAttrib(
vertex_array, position_location);
glVertexArrayAttribFormat(
vertex_array, position_location,
3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(
vertex_array, position_location,
position_bindindex);
glVertexArrayVertexBuffer(
vertex_array, position_bindindex,
position_buffer, 0, sizeof(GLfloat) * 3);
glEnableVertexArrayAttrib(
vertex_array, normal_location);
glVertexArrayAttribFormat(
vertex_array, normal_location,
3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(
vertex_array, normal_location,
normal_bindindex);
glVertexArrayVertexBuffer(
vertex_array, normal_bindindex,
normal_buffer, 0, sizeof(GLfloat) * 3);
glEnableVertexArrayAttrib(
vertex_array, uv_location);
glVertexArrayAttribFormat(
vertex_array, uv_location,
2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(
vertex_array, uv_location,
uv_bindindex);
glVertexArrayVertexBuffer(
vertex_array, uv_bindindex,
uv_buffer, 0, sizeof(GLfloat) * 2);
glClearColor(0.6, 0.8, 0.8, 1.0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(vertex_array);
glDrawArrays(GL_TRIANGLES, 0, model.num_vertices / 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
free_matrix(mat_mv);
free_matrix(mat_mvp);
glfwTerminate();
return EXIT_SUCCESS;
}
初音ミクのモデルをMetasequoiaを使って以下の設定で書きだします。
モデルに付属しているテクスチャ画像を24bitビットマップに変換します。例えばPhotoshopでは以下のように設定します。
プログラムを実行すると以下のように表示されます。
ついに初音ミクが表示されました。