🔥

OpenGLで3Dキューブにテクスチャを適用し回転させる方法

2024/11/12に公開

###本記事では、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