🔥
OpenGLで3Dキューブにテクスチャを適用し回転させる方法
###本記事では、C++とOpenGLを使って3Dキューブにテクスチャを貼り付け、カメラを通してキューブが回転する様子を実現するプログラムを紹介します。ここではGLFWとGLEWを利用してOpenGLの設定を行い、シェーダープログラムによって3Dキューブの描画を実装しています。
1. 必要なヘッダーのインクルード
このコードでは、OpenGLの基本機能に加え、GLFW、GLEW、GLMとstb_imageを使います。これにより、ウィンドウ生成、座標変換、テクスチャの読み込みが可能です。
#include <iostream>
#include <sstream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include "stb_image/stb_image.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
2. シェーダープログラムの定義
頂点シェーダーとフラグメントシェーダーを定義します。頂点シェーダーでは、キューブの位置を表すmodel行列、カメラの視点を表すview行列、投影を制御するprojection行列を使って座標を変換しています。
const GLchar* vertexShaderSrc = R"(
#version 330 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(pos, 1.0f);
TexCoord = texCoord;
}
)";
const GLchar* fragmentShaderSrc = R"(
#version 330 core
in vec2 TexCoord;
uniform sampler2D texSampler1;
out vec4 frag_color;
void main()
{
frag_color = texture(texSampler1, TexCoord);
}
)";
3. キューブの頂点データとテクスチャ座標の設定
キューブの各面の頂点データと対応するテクスチャ座標を定義します。これにより、テクスチャ画像がキューブの面に適用されます。
GLfloat vertices[] = {
// position // tex coords
// front face
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
// back face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
// left face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
// right face
1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
// top face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
// bottom face
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
};
4. テクスチャの読み込みと設定
stb_imageライブラリを使用してテクスチャ画像を読み込みます。これにより、キューブに「.jpg」の画像が貼り付けられます。
GLuint mTextrue;
int width, height, components;
unsigned char* imageData = stbi_load("textures/.jpg", &width, &height, &components, STBI_rgb_alpha);
glGenTextures(1, &mTextrue);
glBindTexture(GL_TEXTURE_2D, mTextrue);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(imageData);
5. モデル、ビュー、投影行列の設定
model行列はキューブの回転や位置を、view行列はカメラの位置を、projection行列は投影方法を制御します。これにより、キューブが3D空間で回転し、視点や投影が調整されます。
glm::mat4 model(1.0), view(1.0), projection(1.0);
model = glm::translate(model, cubePos) * glm::rotate(model, glm::radians(cubeAngle), glm::vec3(0.0f, 1.0f, 0.0f));
view = glm::lookAt(camPos, targetPos, up);
projection = glm::perspective(glm::radians(45.0f), float(Widht) / float(Height), 0.1f, 100.0f);
6. 描画ループ
メインループでは、画面のクリア、シェーダーの使用、行列の適用、キューブの描画が行われます。また、時間経過によりキューブが回転するようにしています。
while (!glfwWindowShouldClose(gWindow))
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
cubeAngle += (float)(deltaTime * 1.0f);
glUniformMatrix4fv(ModelLoc, 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSwapBuffers(gWindow);
}
7. クリーンアップ
終了時に、使用したテクスチャ、シェーダー、バッファを削除してリソースを解放します。
glDeleteTextures(1, &mTextrue);
glDeleteProgram(shaderProgram);
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
全体のコード
#include <iostream>
#include <sstream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include"stb_image/stb_image.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
int Widht = 800,Height = 600;
const GLchar* vertexShaderSrc =
"#version 330 core \n"
"layout (location = 0) in vec3 pos;"
"layout (location = 1) in vec2 texCoord;"
"out vec2 TexCoord;"
"uniform mat4 model;"
"uniform mat4 view;"
"uniform mat4 projection;"
"void main()"
"{"
" gl_Position = projection * view * model * vec4(pos, 1.0f);"
" TexCoord = texCoord; "
"}";
const GLchar* fragmentShaderSrc =
"#version 330 core \n"
"in vec2 TexCoord;"
"uniform vec4 vertColor;"
"out vec4 frag_color;"
"uniform sampler2D texSampler1;"
"void main()"
"{"
" frag_color = texture(texSampler1, TexCoord);"
"}";
void glfw_onKey(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
void glfw_OnFramebufferSize(GLFWwindow* window, int width, int height)
{
Widht = width;
Height = height;
glViewport(0, 0, Widht, Height);
}
int main()
{
glfwInit();
GLFWwindow* gWindow = glfwCreateWindow(Widht, Height, "Window", NULL, NULL);
glfwMakeContextCurrent(gWindow);
glewExperimental = GL_TRUE;
glewInit();
glfwSetKeyCallback(gWindow, glfw_onKey);
glfwSetFramebufferSizeCallback(gWindow, glfw_OnFramebufferSize);
glClearColor(0.23f, 0.38f, 0.47f, 1.0f);
glViewport(0, 0, Widht, Height);
glEnable(GL_DEPTH_TEST);
// 1. Set up an array of vectex data for a cube with index buffer data
GLfloat vertices[] = {
// position // tex coords
// front face
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
// back face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
// left face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
// right face
1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
// top face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
// bottom face
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
};
GLuint vbo, vao;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5* sizeof(GLfloat), (GLvoid*)(0));
glEnableVertexAttribArray(0);
// Texture Coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
//////////////////////////////////////////////////////////////////
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertexShaderSrc, NULL);
glCompileShader(vs);
GLint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragmentShaderSrc, NULL);
glCompileShader(fs);
GLint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vs);
glAttachShader(shaderProgram, fs);
glLinkProgram(shaderProgram);
GLchar infoLog[512];
glGetProgramInfoLog(shaderProgram, sizeof(infoLog), NULL, infoLog);
std::cout << infoLog << std::endl;
glDeleteShader(vs);
glDeleteShader(fs);
///////////////////////////////////////////
bool generateMipMaps = true;
GLuint mTextrue;
std::string fileName = "textures/.jpg";
int width, height, components;
unsigned char* imageData = stbi_load(fileName.c_str(),
&width, &height, &components, STBI_rgb_alpha);
glGenTextures(1, &mTextrue);
glBindTexture(GL_TEXTURE_2D, mTextrue);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, imageData);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(imageData);
glBindTexture(GL_TEXTURE_2D, 0);
double lastTime = glfwGetTime();
glm::vec3 cubePos = glm::vec3(0.0f, 0.0f, -5.0f);
float cubeAngle = 0.0f;
while (!glfwWindowShouldClose(gWindow))
{
double currentTime = glfwGetTime();
double deltaTime = currentTime - lastTime;
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
////////////////////////////////////////
glm::mat4 model(1.0), view(1.0), projection(1.0);
cubeAngle += (float)(deltaTime * 1.0f);
if (cubeAngle >= 360.0f)cubeAngle = 0.0f;
model = glm::translate(model, cubePos) * glm::rotate(model, glm::radians(cubeAngle),
glm::vec3(0.0f, 1.0f, 0.0f));
glm::vec3 camPos(0.0f, 0.0f, 0.0f);
glm::vec3 targetPos(0.0f, 0.0f, -1.0f);
glm::vec3 up(0.0f, 1.0f, 0.0f);
view = glm::lookAt(camPos, targetPos, up);
projection = glm::perspective(glm::radians(45.0f),
float(Widht)/float(Height), 0.1f, 100.0f);
///////////////////////////////////////
glUseProgram(shaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextrue);
GLint TexLoc = glGetUniformLocation(shaderProgram, "texSampler1");
glUniform1i(TexLoc, 0);
GLint ModelLoc = glGetUniformLocation(shaderProgram, "model");
glUniformMatrix4fv(ModelLoc, 1, GL_FALSE, glm::value_ptr(model));
GLint ViewlLoc = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(ViewlLoc, 1, GL_FALSE, glm::value_ptr(view));
GLint ProjectionlLoc = glGetUniformLocation(shaderProgram, "projection");
glUniformMatrix4fv(ProjectionlLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glfwSwapBuffers(gWindow);
}
glDeleteTextures(1, &mTextrue);
glDeleteProgram(shaderProgram);
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}
Discussion