CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(OpenGLShapes)
set(CMAKE_CXX_STANDARD 17)
# GLFW
find_package(glfw3 REQUIRED)
# OpenGL
find_package(OpenGL REQUIRED)
# GLEW(手動で入れた場合)
find_path(GLEW_INCLUDE_DIR GL/glew.h)
find_library(GLEW_LIBRARY NAMES GLEW glew32 glew glew_s)
# stb_image(ヘッダオンリーなので include だけでOK)
include_directories(${CMAKE_SOURCE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/external)
# ソース
add_executable(OpenGLShapes
main.cpp
shapes.cpp
shader.cpp
)
target_include_directories(OpenGLShapes PRIVATE
${GLEW_INCLUDE_DIR}
${OPENGL_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}
)
target_link_libraries(OpenGLShapes
glfw
${GLEW_LIBRARY}
${OPENGL_gl_LIBRARY}
)
# Windows用 GLEW は glew32s のこともあるので注意
main.cpp
#include <iostream> // Required for std::cout
#include <string> // Required for std::string
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include "shader.h"
#include "shapes.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
GLuint createVAO(const std::vector<float>& vertices, const std::vector<unsigned int>& indices) {
GLuint VAO, VBO, EBO;
glGenVertexArrays(1,&VAO);
glGenBuffers(1,&VBO);
glGenBuffers(1,&EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(float), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
// position
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)0);
glEnableVertexAttribArray(0);
// normal
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)(3*sizeof(float)));
glEnableVertexAttribArray(1);
// texcoord
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)(6*sizeof(float)));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
return VAO;
}
void print(const std::string& message) {
std::cout << message << std::endl;
}
int main() {
if (!glfwInit()) {
std::cerr << "GLFW初期化失敗\n";
return -1;
}
GLFWwindow* window = glfwCreateWindow(800,600,"OpenGL 3D Shapes with Textures",NULL,NULL);
if (!window) {
std::cerr << "ウィンドウ作成失敗\n";
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glewExperimental = true;
if(glewInit() != GLEW_OK) {
std::cerr << "GLEW初期化失敗\n";
return -1;
}
glEnable(GL_DEPTH_TEST);
Shader shader("../shader.vert", "../shader.frag");
// シェイプ作成
std::vector<float> boxVertices, sphereVertices, torusVertices;
std::vector<unsigned int> boxIndices, sphereIndices, torusIndices;
createBox(boxVertices, boxIndices);
createSphere(sphereVertices, sphereIndices);
createTorus(torusVertices, torusIndices);
GLuint vaoBox = createVAO(boxVertices, boxIndices);
GLuint vaoSphere = createVAO(sphereVertices, sphereIndices);
GLuint vaoTorus = createVAO(torusVertices, torusIndices);
// テクスチャ読み込み関数
auto loadTexture = [](const char* path) -> GLuint {
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true); // テクスチャ上下反転して読み込み(OpenGL標準に合わせる)
unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0);
if (!data) {
std::cerr << "Failed to load texture: " << path << std::endl;
return 0;
}
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
GLenum format = GL_RGB;
if (nrChannels == 1)
format = GL_RED;
else if (nrChannels == 3)
format = GL_RGB;
else if (nrChannels == 4)
format = GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
return textureID;
};
GLuint texBox = loadTexture("../texture_1.png");
GLuint texSphere = loadTexture("../texture_2.png");
GLuint texTorus = loadTexture("../texture_3.png");
if (texBox == 0 || texSphere == 0 || texTorus == 0) {
std::cerr << "テクスチャの読み込みに失敗しました。\n";
return -1;
}
shader.use();
shader.setInt("texture1", 0);
//----------------------mouse----------------------------------
double mouseX, mouseY;
int winW, winH;
//----------------------mouse----------------------------------
// 行列初期化
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.f/600.f, 0.1f, 100.f);
glm::mat4 view = glm::lookAt(glm::vec3(3,3,3), glm::vec3(0,0,0), glm::vec3(0,1,0));
while(!glfwWindowShouldClose(window)) {
//----------------------mouse----------------------------------
glfwGetCursorPos(window, &mouseX, &mouseY);
glfwGetWindowSize(window, &winW, &winH);
//mouseY = winH - mouseY; // 上下反転
mouseX = mouseX/winW; //
mouseY = mouseY/winH; //
GLint mouseLoc = glGetUniformLocation(shader.ID, "u_mouse");
glUniform2f(mouseLoc, (float)mouseX, (float)mouseY);
//print(std::string("mouseX=") + std::to_string(mouseX));
//----------------------mouse----------------------------------
// 行列初期化
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.f/600.f, 0.1f, 100.f);
glm::mat4 view = glm::lookAt(glm::vec3(3+mouseX*5,3+mouseY*5,3), glm::vec3(0,0,0), glm::vec3(0,1,0));
glClearColor(0.1f,0.1f,0.1f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use();
shader.setMat4("projection", glm::value_ptr(projection));
shader.setMat4("view", glm::value_ptr(view));
// ボックス描画
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-2.0f,0.0f,0.0f));
shader.setMat4("model", glm::value_ptr(model));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texBox);
glBindVertexArray(vaoBox);
glDrawElements(GL_TRIANGLES, (GLsizei)boxIndices.size(), GL_UNSIGNED_INT, 0);
// 球描画
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f,0.0f,0.0f));
shader.setMat4("model", glm::value_ptr(model));
glBindTexture(GL_TEXTURE_2D, texSphere);
glBindVertexArray(vaoSphere);
glDrawElements(GL_TRIANGLES, (GLsizei)sphereIndices.size(), GL_UNSIGNED_INT, 0);
// トーラス描画
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(2.0f,0.0f,0.0f));
shader.setMat4("model", glm::value_ptr(model));
glBindTexture(GL_TEXTURE_2D, texTorus);
glBindVertexArray(vaoTorus);
glDrawElements(GL_TRIANGLES, (GLsizei)torusIndices.size(), GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
// 解放
glDeleteVertexArrays(1, &vaoBox);
glDeleteVertexArrays(1, &vaoSphere);
glDeleteVertexArrays(1, &vaoTorus);
glDeleteTextures(1, &texBox);
glDeleteTextures(1, &texSphere);
glDeleteTextures(1, &texTorus);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}shader.cpp
#include "shader.h"
#include <fstream>
#include <sstream>
#include <iostream>
Shader::Shader(const char* vertexPath, const char* fragmentPath) {
std::string vertexCode, fragmentCode;
std::ifstream vShaderFile, fShaderFile;
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
vShaderFile.close();
fShaderFile.close();
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
} catch(std::ifstream::failure& e) {
std::cerr << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ\n";
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
GLuint vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
void Shader::use() {
glUseProgram(ID);
}
void Shader::setBool(const std::string &name, bool value) const {
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void Shader::setInt(const std::string &name, int value) const {
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::setFloat(const std::string &name, float value) const {
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::setVec2(const std::string &name, float x, float y) const {
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}
void Shader::setVec3(const std::string &name, float x, float y, float z) const {
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}
void Shader::setMat4(const std::string &name, const float* mat) const {
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, mat);
}
void Shader::checkCompileErrors(GLuint shader, std::string type) {
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM") {
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cerr << "| ERROR::SHADER-COMPILATION_ERROR of type: " << type << "\n"
<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
} else {
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cerr << "| ERROR::PROGRAM-LINKING_ERROR of type: " << type << "\n"
<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
shader.frag
#version 330 core
in vec2 TexCoord;
in vec3 Normal;
in vec3 FragPos;
out vec4 FragColor;
uniform vec2 u_mouse; //
uniform sampler2D texture1;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
// Lighting
vec3 ambient = 0.2 * texture(texture1, TexCoord).rgb;
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * texture(texture1, TexCoord).rgb;
vec3 result = ambient + diffuse;
FragColor = vec4(result, 1.0);
}
shader.h
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <GL/glew.h>
class Shader {
public:
GLuint ID;
Shader(const char* vertexPath, const char* fragmentPath);
void use();
void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const;
void setVec2(const std::string &name, float x, float y) const;
void setVec3(const std::string &name, float x, float y, float z) const;
void setMat4(const std::string &name, const float* mat) const;
private:
void checkCompileErrors(GLuint shader, std::string type);
};
#endif
shader.vert
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 aNormal;
out vec2 TexCoord;
out vec3 Normal;
out vec3 FragPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoord = aTexCoord;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
shapes.cpp
#include "shapes.hpp"
#include <cmath>
void createBox(std::vector<float>& vertices, std::vector<unsigned int>& indices) {
vertices = {
// positions // texcoords // normals
-1, -1, -1, 0, 0, 0, 0, -1,
1, -1, -1, 1, 0, 0, 0, -1,
1, 1, -1, 1, 1, 0, 0, -1,
-1, 1, -1, 0, 1, 0, 0, -1,
-1, -1, 1, 0, 0, 0, 0, 1,
1, -1, 1, 1, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0, 0, 1,
-1, 1, 1, 0, 1, 0, 0, 1,
-1, -1, -1, 0, 0, -1, 0, 0,
-1, 1, -1, 1, 0, -1, 0, 0,
-1, 1, 1, 1, 1, -1, 0, 0,
-1, -1, 1, 0, 1, -1, 0, 0,
1, -1, -1, 0, 0, 1, 0, 0,
1, 1, -1, 1, 0, 1, 0, 0,
1, 1, 1, 1, 1, 1, 0, 0,
1, -1, 1, 0, 1, 1, 0, 0,
-1, -1, -1, 0, 0, 0, -1, 0,
-1, -1, 1, 1, 0, 0, -1, 0,
1, -1, 1, 1, 1, 0, -1, 0,
1, -1, -1, 0, 1, 0, -1, 0,
-1, 1, -1, 0, 0, 0, 1, 0,
-1, 1, 1, 1, 0, 0, 1, 0,
1, 1, 1, 1, 1, 0, 1, 0,
1, 1, -1, 0, 1, 0, 1, 0,
};
indices = {
0, 1, 2, 2, 3, 0, // front
4, 5, 6, 6, 7, 4, // back
8, 9, 10, 10, 11, 8, // left
12, 13, 14, 14, 15, 12, // right
16, 17, 18, 18, 19, 16, // bottom
20, 21, 22, 22, 23, 20 // top
};
}
void createSphere(std::vector<float>& vertices, std::vector<unsigned int>& indices, float radius, int sectorCount, int stackCount) {
vertices.clear();
indices.clear();
for (int i = 0; i <= stackCount; ++i) {
float stackAngle = M_PI / 2 - i * (M_PI / stackCount);
float xy = radius * cosf(stackAngle);
float z = radius * sinf(stackAngle);
for (int j = 0; j <= sectorCount; ++j) {
float sectorAngle = j * 2 * M_PI / sectorCount;
float x = xy * cosf(sectorAngle);
float y = xy * sinf(sectorAngle);
float u = (float)j / sectorCount;
float v = (float)i / stackCount;
float nx = x / radius;
float ny = y / radius;
float nz = z / radius;
vertices.insert(vertices.end(), {x, y, z, u, v, nx, ny, nz});
}
}
for (int i = 0; i < stackCount; ++i) {
for (int j = 0; j < sectorCount; ++j) {
unsigned int first = i * (sectorCount + 1) + j;
unsigned int second = first + sectorCount + 1;
indices.insert(indices.end(), {
first, second, first + 1,
second, second + 1, first + 1
});
}
}
}
void createTorus(std::vector<float>& vertices, std::vector<unsigned int>& indices, float innerRadius, float outerRadius, int nsides, int rings) {
vertices.clear();
indices.clear();
for (int i = 0; i <= rings; ++i) {
float phi = 2 * M_PI * i / rings;
float cosPhi = cosf(phi);
float sinPhi = sinf(phi);
for (int j = 0; j <= nsides; ++j) {
float theta = 2 * M_PI * j / nsides;
float cosTheta = cosf(theta);
float sinTheta = sinf(theta);
float x = (outerRadius + innerRadius * cosTheta) * cosPhi;
float y = (outerRadius + innerRadius * cosTheta) * sinPhi;
float z = innerRadius * sinTheta;
float u = (float)i / rings;
float v = (float)j / nsides;
float nx = cosTheta * cosPhi;
float ny = cosTheta * sinPhi;
float nz = sinTheta;
vertices.insert(vertices.end(), {x, y, z, u, v, nx, ny, nz});
}
}
for (int i = 0; i < rings; ++i) {
for (int j = 0; j < nsides; ++j) {
unsigned int first = i * (nsides + 1) + j;
unsigned int second = first + nsides + 1;
indices.insert(indices.end(), {
first, second, first + 1,
second, second + 1, first + 1
});
}
}
}
shapes.hpp
#ifndef SHAPES_HPP
#define SHAPES_HPP
#include <vector>
void createBox(std::vector<float>& vertices, std::vector<unsigned int>& indices);
void createSphere(std::vector<float>& vertices, std::vector<unsigned int>& indices, float radius = 1.0f, int sectorCount = 36, int stackCount = 18);
void createTorus(std::vector<float>& vertices, std::vector<unsigned int>& indices, float ringRadius = 0.7f, float tubeRadius = 0.3f, int ringSegments = 36, int tubeSegments = 18);
#endif
external/stb_image.h https://github.com/nothings/stb/blob/master/stb_image.h
texture_1.png

texture_2.png

texture_3.png

cmd
furcr@furcraea_built MINGW64 ~
$ cd c:
furcr@furcraea_built MINGW64 /c
$ cd glsl_sample
furcr@furcraea_built MINGW64 /c/glsl_sample
$ cd glsl_sample_233_3texture
furcr@furcraea_built MINGW64 /c/glsl_sample/glsl_sample_233_3texture
$ cd build
furcr@furcraea_built MINGW64 /c/glsl_sample/glsl_sample_233_3texture/build
$ cmake -G "MSYS Makefiles" ..
-- The C compiler identification is GNU 15.1.0
-- The CXX compiler identification is GNU 15.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/mingw64/bin/cc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Found OpenGL: opengl32
-- Configuring done (1.9s)
-- Generating done (0.0s)
-- Build files have been written to: C:/glsl_sample/glsl_sample_233_3texture/build
furcr@furcraea_built MINGW64 /c/glsl_sample/glsl_sample_233_3texture/build
$ mingw32-make
[ 25%] Building CXX object CMakeFiles/OpenGLShapes.dir/main.cpp.obj
[ 50%] Building CXX object CMakeFiles/OpenGLShapes.dir/shapes.cpp.obj
[ 75%] Building CXX object CMakeFiles/OpenGLShapes.dir/shader.cpp.obj
[100%] Linking CXX executable OpenGLShapes.exe
[100%] Built target OpenGLShapes
furcr@furcraea_built MINGW64 /c/glsl_sample/glsl_sample_233_3texture/build
$ ./OpenGLShapes


