code re-work: normal mapping.

This commit is contained in:
Joey de Vries
2017-04-23 16:02:02 +02:00
parent 919084ba39
commit b02e4ea394
3 changed files with 190 additions and 193 deletions

View File

@@ -3,11 +3,7 @@ out vec4 FragColor;
in VS_OUT { in VS_OUT {
vec3 FragPos; vec3 FragPos;
vec3 Normal;
vec2 TexCoords; vec2 TexCoords;
vec3 Tangent;
vec3 Bitangent;
mat3 TBN;
vec3 TangentLightPos; vec3 TangentLightPos;
vec3 TangentViewPos; vec3 TangentViewPos;
vec3 TangentFragPos; vec3 TangentFragPos;
@@ -19,30 +15,22 @@ uniform sampler2D normalMap;
uniform vec3 lightPos; uniform vec3 lightPos;
uniform vec3 viewPos; uniform vec3 viewPos;
uniform bool normalMapping;
void main() void main()
{ {
vec3 normal = fs_in.Normal; // obtain normal from normal map in range [0,1]
mat3 tbn; vec3 normal = texture(normalMap, fs_in.TexCoords).rgb;
// Obtain normal from normal map in range [0,1] // transform normal vector to range [-1,1]
normal = texture(normalMap, fs_in.TexCoords).rgb; normal = normalize(normal * 2.0 - 1.0); // this normal is in tangent space
// Transform normal vector to range [-1,1]
normal = normalize(normal * 2.0 - 1.0);
// Then transform normal in tangent space to world-space via TBN matrix
// tbn = mat3(fs_in.Tangent, fs_in.Bitangent, fs_in.Normal); // TBN calculated in fragment shader
// normal = normalize(tbn * normal); // This works!
// normal = normalize(fs_in.TBN * normal); // This gives incorrect results
// Get diffuse color // get diffuse color
vec3 color = texture(diffuseMap, fs_in.TexCoords).rgb; vec3 color = texture(diffuseMap, fs_in.TexCoords).rgb;
// Ambient // ambient
vec3 ambient = 0.1 * color; vec3 ambient = 0.1 * color;
// Diffuse // diffuse
vec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos); vec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos);
float diff = max(dot(lightDir, normal), 0.0); float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * color; vec3 diffuse = diff * color;
// Specular // specular
vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos); vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
vec3 reflectDir = reflect(-lightDir, normal); vec3 reflectDir = reflect(-lightDir, normal);
vec3 halfwayDir = normalize(lightDir + viewDir); vec3 halfwayDir = normalize(lightDir + viewDir);

View File

@@ -1,17 +1,13 @@
#version 330 core #version 330 core
layout (location = 0) in vec3 position; layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 normal; layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 texCoords; layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 tangent; layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 bitangent; layout (location = 4) in vec3 aBitangent;
out VS_OUT { out VS_OUT {
vec3 FragPos; vec3 FragPos;
vec3 Normal;
vec2 TexCoords; vec2 TexCoords;
vec3 Tangent;
vec3 Bitangent;
mat3 TBN;
vec3 TangentLightPos; vec3 TangentLightPos;
vec3 TangentViewPos; vec3 TangentViewPos;
vec3 TangentFragPos; vec3 TangentFragPos;
@@ -26,24 +22,19 @@ uniform vec3 viewPos;
void main() void main()
{ {
gl_Position = projection * view * model * vec4(position, 1.0f); vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.FragPos = vec3(model * vec4(position, 1.0)); vs_out.TexCoords = aTexCoords;
vs_out.TexCoords = texCoords;
mat3 normalMatrix = transpose(inverse(mat3(model))); mat3 normalMatrix = transpose(inverse(mat3(model)));
vs_out.Normal = normalize(normalMatrix * normal); vec3 T = normalize(normalMatrix * aTangent);
vec3 N = normalize(normalMatrix * aNormal);
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(normalMatrix * normal);
T = normalize(T - dot(T, N) * N); T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T); vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
vs_out.TBN = TBN;
mat3 TBN = transpose(mat3(T, B, N));
vs_out.TangentLightPos = TBN * lightPos; vs_out.TangentLightPos = TBN * lightPos;
vs_out.TangentViewPos = TBN * viewPos; vs_out.TangentViewPos = TBN * viewPos;
vs_out.TangentFragPos = TBN * vs_out.FragPos; vs_out.TangentFragPos = TBN * vs_out.FragPos;
vs_out.Tangent = T; gl_Position = projection * view * model * vec4(aPos, 1.0);
vs_out.Bitangent = B;
} }

View File

@@ -1,163 +1,175 @@
// Std. Includes #include <glad/glad.h>
#include <string>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <stb_image.h>
// GL includes
#include <learnopengl/shader.h>
#include <learnopengl/camera.h>
#include <learnopengl/model.h>
// GLM Mathemtics
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
// Other Libs
#include <SOIL.h>
#include <learnopengl/filesystem.h> #include <learnopengl/filesystem.h>
#include <learnopengl/shader.h>
#include <learnopengl/camera.h>
#include <learnopengl/model.h>
// Properties #include <iostream>
const GLuint SCR_WIDTH = 800, SCR_HEIGHT = 600;
// Function prototypes void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos); void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void Do_Movement(); void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
GLuint loadTexture(GLchar const * path); void processInput(GLFWwindow *window);
void RenderQuad(); unsigned int loadTexture(const char *path);
void renderQuad();
// Camera // settings
const unsigned int SCR_WIDTH = 1280;
const unsigned int SCR_HEIGHT = 720;
// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = (float)SCR_WIDTH / 2.0;
float lastY = (float)SCR_HEIGHT / 2.0;
bool firstMouse = true;
GLfloat deltaTime = 0.0f; // timing
GLfloat lastFrame = 0.0f; float deltaTime = 0.0f;
float lastFrame = 0.0f;
// The MAIN function, from here we start our application and run our Game loop
int main() int main()
{ {
// Init GLFW // glfw: initialize and configure
// ------------------------------
glfwInit(); glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed // glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
if (window == NULL)
// Set the required callback functions {
glfwSetKeyCallback(window, key_callback); std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback); glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback); glfwSetScrollCallback(window, scroll_callback);
// Options // tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// Initialize GLEW to setup the OpenGL Function pointers // glad: load all OpenGL function pointers
glewExperimental = GL_TRUE; // ---------------------------------------
glewInit(); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// Define the viewport dimensions // configure global opengl state
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); // -----------------------------
// Setup some OpenGL options
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
// Setup and compile our shaders // build and compile shaders
Shader shader("normal_mapping.vs", "normal_mapping.frag"); // -------------------------
Shader shader("4.normal_mapping.vs", "4.normal_mapping.fs");
// Load textures // load textures
GLuint diffuseMap = loadTexture(FileSystem::getPath("resources/textures/brickwall.jpg").c_str()); // -------------
GLuint normalMap = loadTexture(FileSystem::getPath("resources/textures/brickwall_normal.jpg").c_str()); unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/brickwall.jpg").c_str());
unsigned int normalMap = loadTexture(FileSystem::getPath("resources/textures/brickwall_normal.jpg").c_str());
// Set texture units // shader configuration
shader.Use(); // --------------------
glUniform1i(glGetUniformLocation(shader.Program, "diffuseMap"), 0); shader.use();
glUniform1i(glGetUniformLocation(shader.Program, "normalMap"), 1); shader.setInt("diffuseMap", 0);
shader.setInt("normalMap", 1);
// Light position // lighting info
// -------------
glm::vec3 lightPos(0.5f, 1.0f, 0.3f); glm::vec3 lightPos(0.5f, 1.0f, 0.3f);
// Game loop // render loop
// -----------
while (!glfwWindowShouldClose(window)) while (!glfwWindowShouldClose(window))
{ {
// Set frame time // per-frame time logic
GLfloat currentFrame = glfwGetTime(); // --------------------
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame; deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame; lastFrame = currentFrame;
// Check and call events // input
glfwPollEvents(); // -----
Do_Movement(); processInput(window);
// Clear the colorbuffer // render
// ------
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Configure view/projection matrices // configure view/projection matrices
shader.Use();
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); glm::mat4 view = camera.GetViewMatrix();
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); shader.use();
// Render normal-mapped quad shader.setMat4("projection", projection);
shader.setMat4("view", view);
// render normal-mapped quad
glm::mat4 model; glm::mat4 model;
model = glm::rotate(model, (GLfloat)glfwGetTime() * -0.1f, glm::normalize(glm::vec3(1.0, 0.0, 1.0))); // Rotates the quad to show normal mapping works in all directions model = glm::rotate(model, glm::radians((float)glfwGetTime() * -10.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0))); // Rotates the quad to show normal mapping works in all directions
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); shader.setMat4("model", model);
glUniform3fv(glGetUniformLocation(shader.Program, "lightPos"), 1, &lightPos[0]); shader.setVec3("viewPos", camera.Position);
glUniform3fv(glGetUniformLocation(shader.Program, "viewPos"), 1, &camera.Position[0]); shader.setVec3("lightPos", lightPos);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap); glBindTexture(GL_TEXTURE_2D, diffuseMap);
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normalMap); glBindTexture(GL_TEXTURE_2D, normalMap);
RenderQuad(); renderQuad();
// render light source (simply re-renders a smaller plane at the light's position for debugging/visualization) // render light source (simply re-renders a smaller plane at the light's position for debugging/visualization)
model = glm::mat4(); model = glm::mat4();
model = glm::translate(model, lightPos); model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.1f)); model = glm::scale(model, glm::vec3(0.1f));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); shader.setMat4("model", model);
RenderQuad(); renderQuad();
// Swap the buffers // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window); glfwSwapBuffers(window);
glfwPollEvents();
} }
glfwTerminate(); glfwTerminate();
return 0; return 0;
} }
// RenderQuad() Renders a 1x1 quad in NDC // renderQuad() renders a 1x1 quad in NDC
GLuint quadVAO = 0; unsigned int quadVAO = 0;
GLuint quadVBO; unsigned int quadVBO;
void RenderQuad() void renderQuad()
{ {
if (quadVAO == 0) if (quadVAO == 0)
{ {
// positions // positions
glm::vec3 pos1(-1.0, 1.0, 0.0); glm::vec3 pos1(-1.0f, 1.0f, 0.0f);
glm::vec3 pos2(-1.0, -1.0, 0.0); glm::vec3 pos2(-1.0f, -1.0f, 0.0f);
glm::vec3 pos3(1.0, -1.0, 0.0); glm::vec3 pos3( 1.0f, -1.0f, 0.0f);
glm::vec3 pos4(1.0, 1.0, 0.0); glm::vec3 pos4( 1.0f, 1.0f, 0.0f);
// texture coordinates // texture coordinates
glm::vec2 uv1(0.0, 1.0); glm::vec2 uv1(0.0f, 1.0f);
glm::vec2 uv2(0.0, 0.0); glm::vec2 uv2(0.0f, 0.0f);
glm::vec2 uv3(1.0, 0.0); glm::vec2 uv3(1.0f, 0.0f);
glm::vec2 uv4(1.0, 1.0); glm::vec2 uv4(1.0f, 1.0f);
// normal vector // normal vector
glm::vec3 nm(0.0, 0.0, 1.0); glm::vec3 nm(0.0f, 0.0f, 1.0f);
// calculate tangent/bitangent vectors of both triangles // calculate tangent/bitangent vectors of both triangles
glm::vec3 tangent1, bitangent1; glm::vec3 tangent1, bitangent1;
glm::vec3 tangent2, bitangent2; glm::vec3 tangent2, bitangent2;
// - triangle 1 // triangle 1
// ----------
glm::vec3 edge1 = pos2 - pos1; glm::vec3 edge1 = pos2 - pos1;
glm::vec3 edge2 = pos3 - pos1; glm::vec3 edge2 = pos3 - pos1;
glm::vec2 deltaUV1 = uv2 - uv1; glm::vec2 deltaUV1 = uv2 - uv1;
@@ -175,7 +187,8 @@ void RenderQuad()
bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);
bitangent1 = glm::normalize(bitangent1); bitangent1 = glm::normalize(bitangent1);
// - triangle 2 // triangle 2
// ----------
edge1 = pos3 - pos1; edge1 = pos3 - pos1;
edge2 = pos4 - pos1; edge2 = pos4 - pos1;
deltaUV1 = uv3 - uv1; deltaUV1 = uv3 - uv1;
@@ -195,8 +208,8 @@ void RenderQuad()
bitangent2 = glm::normalize(bitangent2); bitangent2 = glm::normalize(bitangent2);
GLfloat quadVertices[] = { float quadVertices[] = {
// Positions // normal // TexCoords // Tangent // Bitangent // positions // normal // texcoords // tangent // bitangent
pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z,
pos2.x, pos2.y, pos2.z, nm.x, nm.y, nm.z, uv2.x, uv2.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, pos2.x, pos2.y, pos2.z, nm.x, nm.y, nm.z, uv2.x, uv2.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z,
pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z,
@@ -205,92 +218,58 @@ void RenderQuad()
pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z,
pos4.x, pos4.y, pos4.z, nm.x, nm.y, nm.z, uv4.x, uv4.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z pos4.x, pos4.y, pos4.z, nm.x, nm.y, nm.z, uv4.x, uv4.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z
}; };
// Setup plane VAO // configure plane VAO
glGenVertexArrays(1, &quadVAO); glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO); glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO); glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO); glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat))); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(8 * sizeof(float)));
glEnableVertexAttribArray(4); glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(11 * sizeof(GLfloat))); glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(11 * sizeof(float)));
} }
glBindVertexArray(quadVAO); glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0); glBindVertexArray(0);
} }
// This function loads a texture from file. Note: texture loading functions like these are usually // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// managed by a 'Resource Manager' that manages all resources (like textures, models, audio). // ---------------------------------------------------------------------------------------------------------
// For learning purposes we'll just define it as a utility function. void processInput(GLFWwindow *window)
GLuint loadTexture(GLchar const * path)
{ {
//Generate texture ID and load texture data if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
GLuint textureID; glfwSetWindowShouldClose(window, true);
glGenTextures(1, &textureID);
int width, height;
unsigned char* image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB);
// Assign texture to ID
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
// Parameters float cameraSpeed = 2.5 * deltaTime;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
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);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
return textureID;
}
#pragma region "User input"
bool keys[1024];
bool keysPressed[1024];
// Moves/alters the camera positions based on user input
void Do_Movement()
{
// Camera controls
if (keys[GLFW_KEY_W])
camera.ProcessKeyboard(FORWARD, deltaTime); camera.ProcessKeyboard(FORWARD, deltaTime);
if (keys[GLFW_KEY_S]) if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime); camera.ProcessKeyboard(BACKWARD, deltaTime);
if (keys[GLFW_KEY_A]) if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime); camera.ProcessKeyboard(LEFT, deltaTime);
if (keys[GLFW_KEY_D]) if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime); camera.ProcessKeyboard(RIGHT, deltaTime);
} }
// Is called whenever a key is pressed/released via GLFW // glfw: whenever the window size changed (by OS or user resize) this callback function executes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) // ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{ {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) // make sure the viewport matches the new window dimensions; note that width and
glfwSetWindowShouldClose(window, GL_TRUE); // height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
if (key >= 0 && key <= 1024)
{
if (action == GLFW_PRESS)
keys[key] = true;
else if (action == GLFW_RELEASE)
{
keys[key] = false;
keysPressed[key] = false;
}
}
} }
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true; // glfw: whenever the mouse moves, this callback is called
// Moves/alters the camera positions based on user input // -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos) void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{ {
if (firstMouse) if (firstMouse)
@@ -300,8 +279,8 @@ void mouse_callback(GLFWwindow* window, double xpos, double ypos)
firstMouse = false; firstMouse = false;
} }
GLfloat xoffset = xpos - lastX; float xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos; float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos; lastX = xpos;
lastY = ypos; lastY = ypos;
@@ -309,9 +288,48 @@ void mouse_callback(GLFWwindow* window, double xpos, double ypos)
camera.ProcessMouseMovement(xoffset, yoffset); camera.ProcessMouseMovement(xoffset, yoffset);
} }
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{ {
camera.ProcessMouseScroll(yoffset); camera.ProcessMouseScroll(yoffset);
} }
#pragma endregion // utility function for loading a 2D texture from file
// ---------------------------------------------------
unsigned int loadTexture(char const * path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
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, format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT); // for this tutorial: use GL_CLAMP_TO_EDGE to prevent semi-transparent borders. Due to interpolation it takes texels from next repeat
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, format == GL_RGBA ? GL_CLAMP_TO_EDGE : 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);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}