Added Normal Maps

This commit is contained in:
OusmBlueNinja 2025-04-01 12:22:57 -05:00
parent 81ec55a229
commit fdddafe980
6 changed files with 8107 additions and 58 deletions

View File

@ -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();

View File

@ -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

Binary file not shown.

Binary file not shown.

BIN
normal.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

7991
vendor/stb/stb_image.h vendored Normal file

File diff suppressed because it is too large Load Diff