Compare commits

...

4 Commits

Author SHA1 Message Date
0e8e98026c Add normal scale factor
+ some minor tweaks: higher shadowmap resolution and light rotate speed
2020-11-18 16:48:30 +01:00
482aa93cce Add new textures with normalmaps
Now each texture has proper normals
2020-11-18 16:29:28 +01:00
83936f66fb Use Tangents for correct normal calculation
The normal maps are looking good now!
2020-11-18 16:04:44 +01:00
fd9317a5f5 Calculate tangents for ObjMesh 2020-11-18 15:30:31 +01:00
36 changed files with 118 additions and 38 deletions

View File

@ -33,17 +33,25 @@ struct Mesh {
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// position attribute // position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
// Normal attribute // Normal attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
// texture coord attribute // texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
// Tangent attribute
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(8 * sizeof(float)));
glEnableVertexAttribArray(3);
// Bitangent attribute
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(11 * sizeof(float)));
glEnableVertexAttribArray(4);
glBindVertexArray(0); glBindVertexArray(0);
} }

View File

@ -39,16 +39,27 @@ private:
std::vector<float> vertexData = std::vector<float>(); std::vector<float> vertexData = std::vector<float>();
// Go through each vertex auto tangents = getTangentsFromVertices(curMesh.Vertices, getIndicesFromFile(path));
for (auto &Vertice : curMesh.Vertices) {
vertexData.emplace_back(Vertice.Position.X); for (int i = 0; i <= curMesh.Vertices.size(); i++) {
vertexData.emplace_back(Vertice.Position.Y); auto vertex = curMesh.Vertices[i];
vertexData.emplace_back(Vertice.Position.Z); auto tangent = tangents.first[i];
vertexData.emplace_back(Vertice.Normal.X); auto bitangent = tangents.second[i];
vertexData.emplace_back(Vertice.Normal.Y);
vertexData.emplace_back(Vertice.Normal.Z); vertexData.emplace_back(vertex.Position.X);
vertexData.emplace_back(Vertice.TextureCoordinate.X); vertexData.emplace_back(vertex.Position.Y);
vertexData.emplace_back(Vertice.TextureCoordinate.Y); vertexData.emplace_back(vertex.Position.Z);
vertexData.emplace_back(vertex.Normal.X);
vertexData.emplace_back(vertex.Normal.Y);
vertexData.emplace_back(vertex.Normal.Z);
vertexData.emplace_back(vertex.TextureCoordinate.X);
vertexData.emplace_back(vertex.TextureCoordinate.Y);
vertexData.emplace_back(tangent.x);
vertexData.emplace_back(tangent.y);
vertexData.emplace_back(tangent.z);
vertexData.emplace_back(bitangent.x);
vertexData.emplace_back(bitangent.y);
vertexData.emplace_back(bitangent.z);
} }
return vertexData; return vertexData;
@ -58,6 +69,58 @@ private:
} }
} }
static std::pair<std::vector<glm::vec3>, std::vector<glm::vec3>> getTangentsFromVertices(const std::vector<objl::Vertex> &vertices, const std::vector<unsigned int> indices) {
// These vectors hold tangents and bitangents in the same way in which the vertices vectors holds vertices:
// Their index in the vector corresponds to indices in the indices vector.
std::vector<glm::vec3> tangents(vertices.size());
std::vector<glm::vec3> bitangents(vertices.size());
// Iterate over all triangles
for (uint index = 0; index < indices.size(); index += 3) {
// Index in vertex buffer
uint i0 = indices[index];
uint i1 = indices[index + 1];
uint i2 = indices[index + 2];
// Inputs
glm::vec3 vertex0 = glm::vec3(vertices[i0].Position.X, vertices[i0].Position.Y, vertices[i0].Position.Z);
glm::vec3 vertex1 = glm::vec3(vertices[i1].Position.X, vertices[i1].Position.Y, vertices[i1].Position.Z);
glm::vec3 vertex2 = glm::vec3(vertices[i2].Position.X, vertices[i2].Position.Y, vertices[i2].Position.Z);
glm::vec2 uv0 = glm::vec2(vertices[i0].TextureCoordinate.X, vertices[i0].TextureCoordinate.Y);
glm::vec2 uv1 = glm::vec2(vertices[i1].TextureCoordinate.X, vertices[i1].TextureCoordinate.Y);
glm::vec2 uv2 = glm::vec2(vertices[i2].TextureCoordinate.X, vertices[i2].TextureCoordinate.Y);
glm::vec3 normal0 = glm::vec3(vertices[i0].Normal.X, vertices[i0].Normal.Y, vertices[i0].Normal.Z);
glm::vec3 normal1 = glm::vec3(vertices[i1].Normal.X, vertices[i1].Normal.Y, vertices[i1].Normal.Z);
glm::vec3 normal2 = glm::vec3(vertices[i2].Normal.X, vertices[i2].Normal.Y, vertices[i2].Normal.Z);
// Edges of the triangle : position delta
glm::vec3 deltaPos1 = vertex1 - vertex0;
glm::vec3 deltaPos2 = vertex2 - vertex0;
// UV delta
glm::vec2 deltaUV1 = uv1 - uv0;
glm::vec2 deltaUV2 = uv2 - uv0;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r;
glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r;
// Add the tangent and bitangent to the current value in the array.
// This is done to get an average in the end.
tangents[i0] += tangent;
tangents[i1] += tangent;
tangents[i2] += tangent;
bitangents[i0] += bitangent;
bitangents[i1] += bitangent;
bitangents[i2] += bitangent;
}
return std::pair<std::vector<glm::vec3>, std::vector<glm::vec3>>(tangents, bitangents);
}
static std::vector<unsigned int> getIndicesFromFile(const std::string &path) { static std::vector<unsigned int> getIndicesFromFile(const std::string &path) {
objl::Loader loader; objl::Loader loader;

View File

@ -64,9 +64,6 @@ struct Texture {
void addNormalmap(const std::string& path, Settings settings) { void addNormalmap(const std::string& path, Settings settings) {
normal_id = loadTexture(path, settings); normal_id = loadTexture(path, settings);
} }
}; };
#endif //ECSGAME_TEXTURE_H #endif //ECSGAME_TEXTURE_H

View File

@ -78,6 +78,7 @@ public:
// TODO: Not always required (not when rendering shadows) - make functions separate? // TODO: Not always required (not when rendering shadows) - make functions separate?
shader.setFloat("diffuseStrength", material.diffuse); shader.setFloat("diffuseStrength", material.diffuse);
shader.setFloat("specularStrength", material.specular); shader.setFloat("specularStrength", material.specular);
shader.setFloat("normalScale", material.normal_scale);
mesh.render(); mesh.render();
} }
@ -268,8 +269,8 @@ public:
int screen_width = 1280; int screen_width = 1280;
int screen_height = 720; int screen_height = 720;
int shadow_width = 2048; int shadow_width = 8192;
int shadow_height = 2048; int shadow_height = 8192;
unsigned int depthMap; unsigned int depthMap;
unsigned int depthMapFBO; unsigned int depthMapFBO;

View File

@ -13,6 +13,8 @@ struct Material {
float diffuse = 0.8; float diffuse = 0.8;
float specular = 0.2; float specular = 0.2;
float normal_scale = 3.0;
}; };
#endif //ECSGAME_MATERIAL_H #endif //ECSGAME_MATERIAL_H

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

Before

Width:  |  Height:  |  Size: 3.9 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 MiB

View File

@ -5,6 +5,7 @@ in mediump vec2 TexCoord;
in mediump vec3 Normal; in mediump vec3 Normal;
in mediump vec3 FragPos; in mediump vec3 FragPos;
in mediump vec4 FragPosLightSpace; in mediump vec4 FragPosLightSpace;
in mediump mat3 TBN;
layout(binding=0) uniform sampler2D shadowMap; layout(binding=0) uniform sampler2D shadowMap;
layout(binding=1) uniform sampler2D tex; layout(binding=1) uniform sampler2D tex;
@ -15,6 +16,7 @@ uniform mediump vec3 cameraPosition;
uniform mediump float diffuseStrength; uniform mediump float diffuseStrength;
uniform mediump float specularStrength; uniform mediump float specularStrength;
uniform mediump float normalScale;
mediump float ShadowCalculation(vec4 fragPosLightSpace) mediump float ShadowCalculation(vec4 fragPosLightSpace)
{ {
@ -46,8 +48,8 @@ void main()
// Get normal from normal map in the range [-1,1] // Get normal from normal map in the range [-1,1]
mediump vec3 map_normal = normalize(texture(normalmap, TexCoord).rgb * 2.0 - 1.0); mediump vec3 map_normal = normalize(texture(normalmap, TexCoord).rgb * 2.0 - 1.0);
map_normal.xy *= normalScale;
mediump vec3 final_normal = map_normal; mediump vec3 final_normal = normalize(TBN * map_normal);
// Alpha Scissors // Alpha Scissors
if(texColor.a < 0.1) if(texColor.a < 0.1)

View File

@ -2,11 +2,14 @@
layout (location = 0) in vec3 aPos; layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 NORMAL; layout (location = 1) in vec3 NORMAL;
layout (location = 2) in vec2 UV; layout (location = 2) in vec2 UV;
layout (location = 3) in vec3 TANGENT;
layout (location = 4) in vec3 BITANGENT;
out vec2 TexCoord; out vec2 TexCoord;
out vec3 Normal; out vec3 Normal;
out vec3 FragPos; out vec3 FragPos;
out vec4 FragPosLightSpace; out vec4 FragPosLightSpace;
out mat3 TBN;
uniform mat4 model; uniform mat4 model;
uniform mat4 view; uniform mat4 view;
@ -20,4 +23,9 @@ void main()
Normal = NORMAL; Normal = NORMAL;
FragPos = vec3(model * vec4(aPos, 1.0)); FragPos = vec3(model * vec4(aPos, 1.0));
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0); FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
vec3 T = normalize(vec3(model * vec4(TANGENT, 0.0)));
vec3 B = normalize(vec3(model * vec4(BITANGENT, 0.0)));
vec3 N = normalize(vec3(model * vec4(NORMAL, 0.0)));
TBN = mat3(T, B, N);
} }

View File

@ -118,38 +118,33 @@ int main() {
monkey->assign<Transform>(); monkey->assign<Transform>();
monkey->assign<LODObjMesh>(std::vector<ObjMesh>{ObjMesh("Resources/Monkey.obj",ObjMesh::Settings(0.0, 8.0, 0.4, 0.6)), monkey->assign<LODObjMesh>(std::vector<ObjMesh>{ObjMesh("Resources/Monkey.obj",ObjMesh::Settings(0.0, 8.0, 0.4, 0.6)),
ObjMesh("Resources/MonkeySimple.obj", ObjMesh::Settings(8.0, 100.0, 0.4, 0.6))}); ObjMesh("Resources/MonkeySimple.obj", ObjMesh::Settings(8.0, 100.0, 0.4, 0.6))});
monkey->assign<Texture>("Resources/Marble.jpg", Texture::Settings(true), false); monkey->assign<Texture>("Resources/Marble010_2K_Color.jpg", Texture::Settings(true), false);
monkey->get<Texture>()->addNormalmap("Resources/Marble010_2K_Normal.jpg", Texture::Settings(true));
monkey->assign<SineAnimation>(glm::vec3(0.0, 0.3, 0.0), 0.5); monkey->assign<SineAnimation>(glm::vec3(0.0, 0.3, 0.0), 0.5);
monkey->assign<Material>(0.6, 0.6); monkey->assign<Material>(0.6, 0.6);
monkey->get<Transform>()->set_origin(glm::vec3(0.0f, 2.0f, -6.0f)); monkey->get<Transform>()->set_origin(glm::vec3(0.0f, 2.0f, -6.0f));
Entity *monkey2 = world->create();
monkey2->assign<Transform>();
monkey2->assign<LODObjMesh>(std::vector<ObjMesh>{ObjMesh("Resources/Monkey.obj",ObjMesh::Settings(0.0, 8.0, 0.4, 0.6)),
ObjMesh("Resources/MonkeySimple.obj", ObjMesh::Settings(8.0, 100.0, 0.4, 0.6))});
monkey2->assign<Texture>("Resources/Marble.jpg", Texture::Settings(true), false);
monkey2->assign<SineAnimation>(glm::vec3(0.0, 0.3, 0.0), 0.5);
monkey2->assign<Material>(0.6, 0.6);
monkey2->get<Transform>()->set_origin(glm::vec3(-5.0f, 2.0f, -20.0f));
Entity *wall1 = world->create(); Entity *wall1 = world->create();
wall1->assign<Transform>(); wall1->assign<Transform>();
wall1->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings())); wall1->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings()));
wall1->assign<Texture>("Resources/Glass.png", Texture::Settings(true), true); wall1->assign<Texture>("Resources/Facade001_2K_Color.png", Texture::Settings(true), true);
wall1->get<Texture>()->addNormalmap("Resources/Facade001_2K_Normal.jpg", Texture::Settings(true));
wall1->assign<Material>(0.2, 0.8); wall1->assign<Material>(0.2, 0.8);
wall1->get<Transform>()->set_origin(glm::vec3(0.0f, 0.0f, -2.0f)); wall1->get<Transform>()->set_origin(glm::vec3(0.0f, 0.0f, -2.0f));
Entity *wall2 = world->create(); Entity *wall2 = world->create();
wall2->assign<Transform>(); wall2->assign<Transform>();
wall2->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings())); wall2->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings()));
wall2->assign<Texture>("Resources/Glass.png", Texture::Settings(true), true); wall2->assign<Texture>("Resources/Facade001_2K_Color.png", Texture::Settings(true), true);
wall2->get<Texture>()->addNormalmap("Resources/Facade001_2K_Normal.jpg", Texture::Settings(true));
wall2->assign<Material>(0.2, 0.8); wall2->assign<Material>(0.2, 0.8);
wall2->get<Transform>()->set_origin(glm::vec3(0.0f, 0.0f, -10.0f)); wall2->get<Transform>()->set_origin(glm::vec3(0.0f, 0.0f, -10.0f));
Entity *wall3 = world->create(); Entity *wall3 = world->create();
wall3->assign<Transform>(); wall3->assign<Transform>();
wall3->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings())); wall3->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings()));
wall3->assign<Texture>("Resources/Glass.png", Texture::Settings(true), true); wall3->assign<Texture>("Resources/Facade001_2K_Color.png", Texture::Settings(true), true);
wall3->get<Texture>()->addNormalmap("Resources/Facade001_2K_Normal.jpg", Texture::Settings(true));
wall3->assign<Material>(0.2, 0.8); wall3->assign<Material>(0.2, 0.8);
wall3->get<Transform>()->set_origin(glm::vec3(4.0f, 0.0f, -6.0f)); wall3->get<Transform>()->set_origin(glm::vec3(4.0f, 0.0f, -6.0f));
wall3->get<Transform>()->rotate(90.0, glm::vec3(0.0, 1.0, 0.0)); wall3->get<Transform>()->rotate(90.0, glm::vec3(0.0, 1.0, 0.0));
@ -157,7 +152,8 @@ int main() {
Entity *wall4 = world->create(); Entity *wall4 = world->create();
wall4->assign<Transform>(); wall4->assign<Transform>();
wall4->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings())); wall4->assign<ObjMesh>(ObjMesh("Resources/Wall.obj", ObjMesh::Settings()));
wall4->assign<Texture>("Resources/Glass.png", Texture::Settings(true), true); wall4->assign<Texture>("Resources/Facade001_2K_Color.png", Texture::Settings(true), true);
wall4->get<Texture>()->addNormalmap("Resources/Facade001_2K_Normal.jpg", Texture::Settings(true));
wall4->assign<Material>(0.2, 0.8); wall4->assign<Material>(0.2, 0.8);
wall4->get<Transform>()->set_origin(glm::vec3(-4.0f, 0.0f, -6.0f)); wall4->get<Transform>()->set_origin(glm::vec3(-4.0f, 0.0f, -6.0f));
wall4->get<Transform>()->rotate(90.0, glm::vec3(0.0, 1.0, 0.0)); wall4->get<Transform>()->rotate(90.0, glm::vec3(0.0, 1.0, 0.0));
@ -165,15 +161,16 @@ int main() {
Entity *ground = world->create(); Entity *ground = world->create();
ground->assign<Transform>(); ground->assign<Transform>();
ground->assign<ObjMesh>(ObjMesh("Resources/Ground.obj", ObjMesh::Settings())); ground->assign<ObjMesh>(ObjMesh("Resources/Ground.obj", ObjMesh::Settings()));
ground->assign<Texture>("Resources/Grass.jpg", Texture::Settings(true), false); ground->assign<Texture>("Resources/Ground003_2K_Color.jpg", Texture::Settings(true), false);
ground->get<Texture>()->addNormalmap("Resources/Grass_Normal.jpg", Texture::Settings(true)); ground->get<Texture>()->addNormalmap("Resources/Ground003_2K_Normal.jpg", Texture::Settings(true));
ground->assign<Material>(1.0, 0.0); ground->assign<Material>(1.0, 0.0);
ground->get<Transform>()->set_origin(glm::vec3(0.0f, 0.0f, 0.0f)); ground->get<Transform>()->set_origin(glm::vec3(0.0f, 0.0f, 0.0f));
Entity *bench = world->create(); Entity *bench = world->create();
bench->assign<Transform>(); bench->assign<Transform>();
bench->assign<ObjMesh>(ObjMesh("Resources/bench.obj", ObjMesh::Settings())); bench->assign<ObjMesh>(ObjMesh("Resources/bench.obj", ObjMesh::Settings()));
bench->assign<Texture>("Resources/Wood.jpg", Texture::Settings(true), false); bench->assign<Texture>("Resources/Wood048_2K_Color.jpg", Texture::Settings(true), false);
bench->get<Texture>()->addNormalmap("Resources/Wood048_2K_Normal.jpg", Texture::Settings(true));
bench->assign<Material>(0.8, 0.2); bench->assign<Material>(0.8, 0.2);
bench->get<Transform>()->set_origin(glm::vec3(8.0f, 0.0f, 0.0f)); bench->get<Transform>()->set_origin(glm::vec3(8.0f, 0.0f, 0.0f));
bench->get<Transform>()->rotate(-90.0, glm::vec3(0.0, 1.0, 0.0)); bench->get<Transform>()->rotate(-90.0, glm::vec3(0.0, 1.0, 0.0));
@ -181,7 +178,8 @@ int main() {
Entity *ring = world->create(); Entity *ring = world->create();
ring->assign<Transform>(); ring->assign<Transform>();
ring->assign<ObjMesh>(ObjMesh("Resources/ring.obj", ObjMesh::Settings())); ring->assign<ObjMesh>(ObjMesh("Resources/ring.obj", ObjMesh::Settings()));
ring->assign<Texture>("Resources/Gold.jpg", Texture::Settings(true), false); ring->assign<Texture>("Resources/Metal007_2K_Color.jpg", Texture::Settings(true), false);
ring->get<Texture>()->addNormalmap("Resources/Metal007_2K_Normal.jpg", Texture::Settings(true));
ring->assign<Material>(0.1, 0.9); ring->assign<Material>(0.1, 0.9);
ring->get<Transform>()->set_origin(glm::vec3(-5.0f, 2.0f, 0.0f)); ring->get<Transform>()->set_origin(glm::vec3(-5.0f, 2.0f, 0.0f));
@ -206,6 +204,7 @@ int main() {
renderSystem->render(world, defaultShader, shadowShader, debugShader); renderSystem->render(world, defaultShader, shadowShader, debugShader);
ring->get<Transform>()->rotate(delta * 100.0, glm::vec3(0.0, 1.0, 0.0)); ring->get<Transform>()->rotate(delta * 100.0, glm::vec3(0.0, 1.0, 0.0));
sun->get<DirectionalLight>()->direction = glm::normalize(glm::vec3(glm::rotate(glm::mat4(1), (float)elapsed_time * 0.3f, glm::vec3(0.0, 1.0, 0.0)) * glm::vec4(1.0, 1.0, 1.0, 0.0)));
/* Swap front and back buffers */ /* Swap front and back buffers */
glfwSwapBuffers(window); glfwSwapBuffers(window);