Added Normal Maps
This commit is contained in:
parent
81ec55a229
commit
fdddafe980
@ -1,6 +1,8 @@
|
|||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
// Static member definitions.
|
// Static member definitions.
|
||||||
GLFWwindow* Engine::window = nullptr;
|
GLFWwindow* Engine::window = nullptr;
|
||||||
@ -14,6 +16,8 @@ float Engine::rotationAngle = 0.0f;
|
|||||||
int Engine::fbWidth = 640;
|
int Engine::fbWidth = 640;
|
||||||
int Engine::fbHeight = 400;
|
int Engine::fbHeight = 400;
|
||||||
|
|
||||||
|
GLuint normalMapTexture = 0; // Global texture for the normal map
|
||||||
|
|
||||||
bool Engine::Init() {
|
bool Engine::Init() {
|
||||||
if (!glfwInit()) {
|
if (!glfwInit()) {
|
||||||
std::cout << "Failed to initialize GLFW\n";
|
std::cout << "Failed to initialize GLFW\n";
|
||||||
@ -45,7 +49,7 @@ bool Engine::Init() {
|
|||||||
glGenFramebuffers(1, &framebuffer);
|
glGenFramebuffers(1, &framebuffer);
|
||||||
ResizeFramebuffer(fbWidth, fbHeight);
|
ResizeFramebuffer(fbWidth, fbHeight);
|
||||||
|
|
||||||
// Setup cube geometry and shaders.
|
// Setup cube geometry, shaders, and load normal map.
|
||||||
if (!SetupScene()) {
|
if (!SetupScene()) {
|
||||||
std::cout << "Failed to set up scene\n";
|
std::cout << "Failed to set up scene\n";
|
||||||
return false;
|
return false;
|
||||||
@ -70,10 +74,10 @@ void Engine::ResizeFramebuffer(int width, int height) {
|
|||||||
int newWidth = width;
|
int newWidth = width;
|
||||||
int newHeight = height;
|
int newHeight = height;
|
||||||
if (currentAspect > targetAspect) {
|
if (currentAspect > targetAspect) {
|
||||||
// The viewport is too wide: adjust width.
|
// Too wide: adjust width.
|
||||||
newWidth = static_cast<int>(height * targetAspect);
|
newWidth = static_cast<int>(height * targetAspect);
|
||||||
} else if (currentAspect < targetAspect) {
|
} else if (currentAspect < targetAspect) {
|
||||||
// The viewport is too tall: adjust height.
|
// Too tall: adjust height.
|
||||||
newHeight = static_cast<int>(width / targetAspect);
|
newHeight = static_cast<int>(width / targetAspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +115,6 @@ void Engine::ResizeFramebuffer(int width, int height) {
|
|||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
|
GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
|
||||||
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
glShaderSource(vertexShader, 1, &vertexSrc, nullptr);
|
glShaderSource(vertexShader, 1, &vertexSrc, nullptr);
|
||||||
@ -151,56 +154,57 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Engine::SetupScene() {
|
bool Engine::SetupScene() {
|
||||||
// Cube vertices: positions and normals.
|
// Updated cube vertices: each vertex has:
|
||||||
|
// position (3), normal (3), texcoord (2), tangent (3) = 11 floats per vertex.
|
||||||
float vertices[] = {
|
float vertices[] = {
|
||||||
// positions // normals
|
// Front face (normal: 0,0,1), tangent: (1,0,0)
|
||||||
// Front face
|
// positions normals texcoords tangent
|
||||||
-0.5f, -0.5f, 0.5f, 0, 0, 1,
|
-0.5f, -0.5f, 0.5f, 0,0,1, 0,0, 1,0,0,
|
||||||
0.5f, -0.5f, 0.5f, 0, 0, 1,
|
0.5f, -0.5f, 0.5f, 0,0,1, 1,0, 1,0,0,
|
||||||
0.5f, 0.5f, 0.5f, 0, 0, 1,
|
0.5f, 0.5f, 0.5f, 0,0,1, 1,1, 1,0,0,
|
||||||
0.5f, 0.5f, 0.5f, 0, 0, 1,
|
0.5f, 0.5f, 0.5f, 0,0,1, 1,1, 1,0,0,
|
||||||
-0.5f, 0.5f, 0.5f, 0, 0, 1,
|
-0.5f, 0.5f, 0.5f, 0,0,1, 0,1, 1,0,0,
|
||||||
-0.5f, -0.5f, 0.5f, 0, 0, 1,
|
-0.5f, -0.5f, 0.5f, 0,0,1, 0,0, 1,0,0,
|
||||||
|
|
||||||
// Back face
|
// Back face (normal: 0,0,-1), tangent: (-1,0,0)
|
||||||
-0.5f, -0.5f, -0.5f, 0, 0, -1,
|
0.5f, -0.5f, -0.5f, 0,0,-1, 0,0, -1,0,0,
|
||||||
0.5f, -0.5f, -0.5f, 0, 0, -1,
|
-0.5f, -0.5f, -0.5f, 0,0,-1, 1,0, -1,0,0,
|
||||||
0.5f, 0.5f, -0.5f, 0, 0, -1,
|
-0.5f, 0.5f, -0.5f, 0,0,-1, 1,1, -1,0,0,
|
||||||
0.5f, 0.5f, -0.5f, 0, 0, -1,
|
-0.5f, 0.5f, -0.5f, 0,0,-1, 1,1, -1,0,0,
|
||||||
-0.5f, 0.5f, -0.5f, 0, 0, -1,
|
0.5f, 0.5f, -0.5f, 0,0,-1, 0,1, -1,0,0,
|
||||||
-0.5f, -0.5f, -0.5f, 0, 0, -1,
|
0.5f, -0.5f, -0.5f, 0,0,-1, 0,0, -1,0,0,
|
||||||
|
|
||||||
// Left face
|
// Left face (normal: -1,0,0), tangent: (0,0,-1)
|
||||||
-0.5f, 0.5f, 0.5f, -1, 0, 0,
|
-0.5f, -0.5f, -0.5f, -1,0,0, 0,0, 0,0,-1,
|
||||||
-0.5f, 0.5f, -0.5f, -1, 0, 0,
|
-0.5f, -0.5f, 0.5f, -1,0,0, 1,0, 0,0,-1,
|
||||||
-0.5f, -0.5f, -0.5f, -1, 0, 0,
|
-0.5f, 0.5f, 0.5f, -1,0,0, 1,1, 0,0,-1,
|
||||||
-0.5f, -0.5f, -0.5f, -1, 0, 0,
|
-0.5f, 0.5f, 0.5f, -1,0,0, 1,1, 0,0,-1,
|
||||||
-0.5f, -0.5f, 0.5f, -1, 0, 0,
|
-0.5f, 0.5f, -0.5f, -1,0,0, 0,1, 0,0,-1,
|
||||||
-0.5f, 0.5f, 0.5f, -1, 0, 0,
|
-0.5f, -0.5f, -0.5f, -1,0,0, 0,0, 0,0,-1,
|
||||||
|
|
||||||
// Right face
|
// Right face (normal: 1,0,0), tangent: (0,0,1)
|
||||||
0.5f, 0.5f, 0.5f, 1, 0, 0,
|
0.5f, -0.5f, 0.5f, 1,0,0, 0,0, 0,0,1,
|
||||||
0.5f, 0.5f, -0.5f, 1, 0, 0,
|
0.5f, -0.5f, -0.5f, 1,0,0, 1,0, 0,0,1,
|
||||||
0.5f, -0.5f, -0.5f, 1, 0, 0,
|
0.5f, 0.5f, -0.5f, 1,0,0, 1,1, 0,0,1,
|
||||||
0.5f, -0.5f, -0.5f, 1, 0, 0,
|
0.5f, 0.5f, -0.5f, 1,0,0, 1,1, 0,0,1,
|
||||||
0.5f, -0.5f, 0.5f, 1, 0, 0,
|
0.5f, 0.5f, 0.5f, 1,0,0, 0,1, 0,0,1,
|
||||||
0.5f, 0.5f, 0.5f, 1, 0, 0,
|
0.5f, -0.5f, 0.5f, 1,0,0, 0,0, 0,0,1,
|
||||||
|
|
||||||
// Top face
|
// Top face (normal: 0,1,0), tangent: (1,0,0)
|
||||||
-0.5f, 0.5f, -0.5f, 0, 1, 0,
|
-0.5f, 0.5f, 0.5f, 0,1,0, 0,0, 1,0,0,
|
||||||
0.5f, 0.5f, -0.5f, 0, 1, 0,
|
0.5f, 0.5f, 0.5f, 0,1,0, 1,0, 1,0,0,
|
||||||
0.5f, 0.5f, 0.5f, 0, 1, 0,
|
0.5f, 0.5f, -0.5f, 0,1,0, 1,1, 1,0,0,
|
||||||
0.5f, 0.5f, 0.5f, 0, 1, 0,
|
0.5f, 0.5f, -0.5f, 0,1,0, 1,1, 1,0,0,
|
||||||
-0.5f, 0.5f, 0.5f, 0, 1, 0,
|
-0.5f, 0.5f, -0.5f, 0,1,0, 0,1, 1,0,0,
|
||||||
-0.5f, 0.5f, -0.5f, 0, 1, 0,
|
-0.5f, 0.5f, 0.5f, 0,1,0, 0,0, 1,0,0,
|
||||||
|
|
||||||
// Bottom face
|
// Bottom face (normal: 0,-1,0), tangent: (1,0,0)
|
||||||
-0.5f, -0.5f, -0.5f, 0, -1, 0,
|
-0.5f, -0.5f, -0.5f, 0,-1,0, 0,0, 1,0,0,
|
||||||
0.5f, -0.5f, -0.5f, 0, -1, 0,
|
0.5f, -0.5f, -0.5f, 0,-1,0, 1,0, 1,0,0,
|
||||||
0.5f, -0.5f, 0.5f, 0, -1, 0,
|
0.5f, -0.5f, 0.5f, 0,-1,0, 1,1, 1,0,0,
|
||||||
0.5f, -0.5f, 0.5f, 0, -1, 0,
|
0.5f, -0.5f, 0.5f, 0,-1,0, 1,1, 1,0,0,
|
||||||
-0.5f, -0.5f, 0.5f, 0, -1, 0,
|
-0.5f, -0.5f, 0.5f, 0,-1,0, 0,1, 1,0,0,
|
||||||
-0.5f, -0.5f, -0.5f, 0, -1, 0
|
-0.5f, -0.5f, -0.5f, 0,-1,0, 0,0, 1,0,0
|
||||||
};
|
};
|
||||||
|
|
||||||
glGenVertexArrays(1, &cubeVAO);
|
glGenVertexArrays(1, &cubeVAO);
|
||||||
@ -209,11 +213,17 @@ bool Engine::SetupScene() {
|
|||||||
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
|
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
// Position attribute.
|
// Position attribute.
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)0);
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
// Normal attribute.
|
// Normal attribute.
|
||||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(3 * sizeof(float)));
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
// Texcoord attribute.
|
||||||
|
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(6 * sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
// Tangent attribute.
|
||||||
|
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(8 * sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(3);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
// Vertex shader source.
|
// Vertex shader source.
|
||||||
@ -221,6 +231,8 @@ bool Engine::SetupScene() {
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
layout(location = 0) in vec3 aPos;
|
layout(location = 0) in vec3 aPos;
|
||||||
layout(location = 1) in vec3 aNormal;
|
layout(location = 1) in vec3 aNormal;
|
||||||
|
layout(location = 2) in vec2 aTexCoords;
|
||||||
|
layout(location = 3) in vec3 aTangent;
|
||||||
|
|
||||||
uniform mat4 model;
|
uniform mat4 model;
|
||||||
uniform mat4 view;
|
uniform mat4 view;
|
||||||
@ -228,40 +240,57 @@ bool Engine::SetupScene() {
|
|||||||
|
|
||||||
out vec3 FragPos;
|
out vec3 FragPos;
|
||||||
out vec3 Normal;
|
out vec3 Normal;
|
||||||
|
out vec2 TexCoords;
|
||||||
|
out vec3 Tangent;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FragPos = vec3(model * vec4(aPos, 1.0));
|
FragPos = vec3(model * vec4(aPos, 1.0));
|
||||||
Normal = mat3(transpose(inverse(model))) * aNormal;
|
Normal = mat3(transpose(inverse(model))) * aNormal;
|
||||||
|
TexCoords = aTexCoords;
|
||||||
|
Tangent = mat3(model) * aTangent;
|
||||||
gl_Position = projection * view * vec4(FragPos, 1.0);
|
gl_Position = projection * view * vec4(FragPos, 1.0);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
// Fragment shader source with red light.
|
// Fragment shader source with normal mapping.
|
||||||
const char* fragmentShaderSrc = R"(
|
const char* fragmentShaderSrc = R"(
|
||||||
#version 330 core
|
#version 330 core
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
in vec3 FragPos;
|
in vec3 FragPos;
|
||||||
in vec3 Normal;
|
in vec3 Normal;
|
||||||
|
in vec2 TexCoords;
|
||||||
|
in vec3 Tangent;
|
||||||
|
|
||||||
uniform vec3 lightPos;
|
uniform vec3 lightPos;
|
||||||
uniform vec3 viewPos;
|
uniform vec3 viewPos;
|
||||||
|
uniform sampler2D normalMap;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
// Obtain the normal from the normal map in range [0,1] and remap to [-1,1].
|
||||||
|
vec3 normMap = texture(normalMap, TexCoords).rgb;
|
||||||
|
normMap = normalize(normMap);
|
||||||
|
|
||||||
|
// Calculate TBN matrix. For simplicity, compute bitangent as cross(normal, tangent).
|
||||||
|
vec3 N = normalize(Normal);
|
||||||
|
vec3 T = normalize(Tangent);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
vec3 perturbedNormal = normalize(TBN * normMap);
|
||||||
|
|
||||||
// Ambient.
|
// Ambient.
|
||||||
float ambientStrength = 0.3;
|
float ambientStrength = 0.5;
|
||||||
vec3 ambient = ambientStrength * vec3(1.0, 0.0, 1.0);
|
vec3 ambient = ambientStrength * vec3(1.0, 0.0, 1.0);
|
||||||
|
|
||||||
// Diffuse.
|
// Diffuse.
|
||||||
vec3 norm = normalize(Normal);
|
|
||||||
vec3 lightDir = normalize(lightPos - FragPos);
|
vec3 lightDir = normalize(lightPos - FragPos);
|
||||||
float diff = max(dot(norm, lightDir), 0.0);
|
float diff = max(dot(perturbedNormal, lightDir), 0.0);
|
||||||
vec3 diffuse = diff * vec3(1.0, 0.0, 0.0);
|
vec3 diffuse = diff * vec3(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
// Specular.
|
// Specular.
|
||||||
float specularStrength = 0.5;
|
float specularStrength = 0.2;
|
||||||
vec3 viewDir = normalize(viewPos - FragPos);
|
vec3 viewDir = normalize(viewPos - FragPos);
|
||||||
vec3 reflectDir = reflect(-lightDir, norm);
|
vec3 reflectDir = reflect(-lightDir, perturbedNormal);
|
||||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
|
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
|
||||||
vec3 specular = specularStrength * spec * vec3(1.0);
|
vec3 specular = specularStrength * spec * vec3(1.0);
|
||||||
|
|
||||||
@ -274,6 +303,30 @@ bool Engine::SetupScene() {
|
|||||||
if (shaderProgram == 0) {
|
if (shaderProgram == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load normal map from "./normal.png"
|
||||||
|
int texWidth, texHeight, texChannels;
|
||||||
|
unsigned char* data = stbi_load("./normal.jpg", &texWidth, &texHeight, &texChannels, 0);
|
||||||
|
if (!data) {
|
||||||
|
std::cout << "Failed to load normal map." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
glGenTextures(1, &normalMapTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, normalMapTexture);
|
||||||
|
GLenum format = (texChannels == 3) ? GL_RGB : GL_RGBA;
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, format, texWidth, texHeight, 0, format, GL_UNSIGNED_BYTE, data);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
// Set texture parameters.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Bind the normal map to texture unit 1.
|
||||||
|
glUseProgram(shaderProgram);
|
||||||
|
glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,6 +348,10 @@ ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projecti
|
|||||||
|
|
||||||
glUniform3f(glGetUniformLocation(shaderProgram, "lightPos"), 0.0f, 20.0f, 0.0f);
|
glUniform3f(glGetUniformLocation(shaderProgram, "lightPos"), 0.0f, 20.0f, 0.0f);
|
||||||
|
|
||||||
|
// Activate texture unit 1 and bind the normal map.
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, normalMapTexture);
|
||||||
|
|
||||||
glBindVertexArray(cubeVAO);
|
glBindVertexArray(cubeVAO);
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
@ -313,6 +370,7 @@ void Engine::Shutdown() {
|
|||||||
glDeleteProgram(shaderProgram);
|
glDeleteProgram(shaderProgram);
|
||||||
glDeleteFramebuffers(1, &framebuffer);
|
glDeleteFramebuffers(1, &framebuffer);
|
||||||
glDeleteTextures(1, &colorTexture);
|
glDeleteTextures(1, &colorTexture);
|
||||||
|
glDeleteTextures(1, &normalMapTexture);
|
||||||
glDeleteRenderbuffers(1, &depthRenderbuffer);
|
glDeleteRenderbuffers(1, &depthRenderbuffer);
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
2
Makefile
2
Makefile
@ -1,6 +1,6 @@
|
|||||||
# Compiler and flags
|
# Compiler and flags
|
||||||
CXX := g++
|
CXX := g++
|
||||||
CXXFLAGS := -std=c++20 -Wall -Wextra -O2 -I/c/msys64/mingw64/include -Ivendor/imgui-docking
|
CXXFLAGS := -std=c++20 -Wall -Wextra -O2 -I/c/msys64/mingw64/include -Ivendor/imgui-docking -Ivendor/stb
|
||||||
|
|
||||||
# Use this to link against the correct import lib
|
# Use this to link against the correct import lib
|
||||||
LDFLAGS := -Llib -lglfw3 -lopengl32 -lglew32 -lglu32
|
LDFLAGS := -Llib -lglfw3 -lopengl32 -lglew32 -lglu32
|
||||||
|
BIN
Three-Labs.exe
BIN
Three-Labs.exe
Binary file not shown.
Binary file not shown.
BIN
normal.jpg
Normal file
BIN
normal.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 360 KiB |
7991
vendor/stb/stb_image.h
vendored
Normal file
7991
vendor/stb/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user