From 25c4e7c9bce92d5fc4b14ad2c768822a4bd4ad98 Mon Sep 17 00:00:00 2001 From: Franciszek Szewczyk Date: Sun, 20 Oct 2024 01:33:17 +0200 Subject: [PATCH] Goroud shading --- resources/shaders/fragment/uber.glsl | 52 ++++++++++++++++-- resources/shaders/vertex/camera_view.glsl | 66 +++++------------------ src/AssetManager/Mesh.cpp | 47 +++++++++++----- 3 files changed, 96 insertions(+), 69 deletions(-) diff --git a/resources/shaders/fragment/uber.glsl b/resources/shaders/fragment/uber.glsl index d46c75f3..a087167c 100644 --- a/resources/shaders/fragment/uber.glsl +++ b/resources/shaders/fragment/uber.glsl @@ -1,9 +1,55 @@ #version 330 core -out vec4 FragColor; +out vec4 FragColor; // Final fragment color -in vec3 VertexColor; // Interpolated color from the vertex shader +in vec3 FragPos; // Fragment position in world space +in vec3 Normal; // Normal in world space + +uniform vec3 viewPos; // Camera position + +// Light structure +struct Light { + vec3 position; // Light position + vec3 ambient; // Ambient light color + vec3 diffuse; // Diffuse light color + vec3 specular; // Specular light color +}; + +uniform int numLights; // Number of active lights +uniform Light lights[16]; // Array of lights (supporting up to 16 point lights) + +// Material structure +struct Material { + vec3 diffuse; // Diffuse material color + vec3 specular; // Specular material color + float shininess; // Shininess factor +}; + +uniform Material material; // Material properties void main() { - FragColor = vec4(VertexColor, 1.0); // Output the interpolated color + vec3 result = vec3(0.0); // Initialize lighting result + + // Process each light source + for (int i = 0; i < numLights; ++i) { + // Ambient component + vec3 ambient = lights[i].ambient * material.diffuse; + + // Diffuse component + vec3 lightDir = normalize(lights[i].position - FragPos); + float diff = max(dot(Normal, lightDir), 0.0); + vec3 diffuse = lights[i].diffuse * diff * material.diffuse; + + // Specular component + vec3 viewDir = normalize(viewPos - FragPos); + vec3 reflectDir = reflect(-lightDir, Normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + vec3 specular = lights[i].specular * spec * material.specular; + + // Accumulate the lighting + result += ambient + diffuse + specular; + } + + FragColor = + vec4(result, 1.0); // Set the final fragment color with full opacity } diff --git a/resources/shaders/vertex/camera_view.glsl b/resources/shaders/vertex/camera_view.glsl index a0ad48bc..2bd3f5a7 100644 --- a/resources/shaders/vertex/camera_view.glsl +++ b/resources/shaders/vertex/camera_view.glsl @@ -1,63 +1,21 @@ #version 330 core -layout(location = 0) in vec3 position; -layout(location = 1) in vec3 normal; +layout(location = 0) in vec3 position; // Vertex position +layout(location = 1) in vec3 normal; // Vertex normal -uniform mat4 modelMatrix; -uniform mat4 viewMatrix; -uniform mat4 projectionMatrix; +uniform mat4 modelMatrix; // Model matrix +uniform mat4 viewMatrix; // View matrix +uniform mat4 projectionMatrix; // Projection matrix -out vec3 FragPos; -out vec3 Normal; -out vec3 VertexColor; // Color output for Gouraud shading - -// Light structure -struct Light { - vec3 position; // Light position - vec3 ambient; - vec3 diffuse; - vec3 specular; -}; - -// Material structure -struct Material { - vec3 diffuse; - vec3 specular; - float shininess; -}; - -uniform int numLights; // Number of active lights -uniform Light lights[16]; // Array of lights (supporting up to 16 point lights) -uniform Material material; // Material properties -uniform vec3 viewPos; // Camera position +out vec3 FragPos; // Fragment position in world space +out vec3 Normal; // Normal in world space void main() { - FragPos = vec3(modelMatrix * vec4(position, 1.0)); // World space position + FragPos = vec3(modelMatrix * + vec4(position, 1.0)); // Transform position to world space Normal = normalize(mat3(transpose(inverse(modelMatrix))) * - normal); // Normal in world space - - // Initialize lighting result for Gouraud shading - vec3 result = vec3(0.0); - vec3 viewDir = - normalize(viewPos - FragPos); // Direction from fragment to view - - // Process each light - for (int i = 0; i < numLights; ++i) { - vec3 ambient = lights[i].ambient * material.diffuse; - - vec3 lightDir = normalize(lights[i].position - FragPos); - float diff = max(dot(Normal, lightDir), 0.0); - vec3 diffuse = lights[i].diffuse * diff * material.diffuse; - - vec3 reflectDir = reflect(-lightDir, Normal); - float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); - vec3 specular = lights[i].specular * spec * material.specular; - - result += ambient + diffuse + specular; // Accumulate the lighting - } - - VertexColor = result; // Pass the computed color to the fragment shader + normal); // Transform normal to world space - gl_Position = - projectionMatrix * viewMatrix * vec4(FragPos, 1.0); // MVP transform + // Transform the vertex position to clip space + gl_Position = projectionMatrix * viewMatrix * vec4(FragPos, 1.0); } diff --git a/src/AssetManager/Mesh.cpp b/src/AssetManager/Mesh.cpp index bd1e34da..d9c7697e 100644 --- a/src/AssetManager/Mesh.cpp +++ b/src/AssetManager/Mesh.cpp @@ -1,11 +1,22 @@ #include #include #include +#include +#include #include // Assuming you are using a simple OBJ loader. You could integrate a library like Assimp for more advanced formats. #include +namespace std { + template <> + struct std::hash { + size_t operator()(const glm::vec3& vec) const { + return std::hash()(vec.x) ^ std::hash()(vec.y) ^ std::hash()(vec.z); + } + }; +} + namespace shkyera { // Constructor that loads the model from a file @@ -20,9 +31,10 @@ Mesh::~Mesh() { glDeleteBuffers(1, &_ebo); } + static std::vector calculateNormals(const std::vector& vertices, const std::vector& indices) { - std::vector> vertexToFaceIndex(vertices.size()); - std::vector faceToNormal(indices.size(), glm::vec3(0.0f)); + std::unordered_map vertexToNormalMap; + std::unordered_map> faceNormals; for (size_t faceIndex = 0; faceIndex < indices.size(); faceIndex += 3) { unsigned int idx0 = indices[faceIndex]; @@ -35,21 +47,32 @@ static std::vector calculateNormals(const std::vector& glm::vec3 edge1 = v1 - v0; glm::vec3 edge2 = v2 - v0; - glm::vec3 normal = glm::normalize(glm::cross(edge1, edge2)); + glm::vec3 faceNormal = glm::normalize(glm::cross(edge1, edge2)); - faceToNormal[faceIndex] = normal; - vertexToFaceIndex[idx0].push_back(faceIndex); - vertexToFaceIndex[idx1].push_back(faceIndex); - vertexToFaceIndex[idx2].push_back(faceIndex); + faceNormals[v0].push_back(faceNormal); + faceNormals[v1].push_back(faceNormal); + faceNormals[v2].push_back(faceNormal); } std::vector vertexToNormal(vertices.size(), glm::vec3(0.0f)); - for(size_t vertex = 0; vertex < vertices.size(); ++vertex) { - for(const auto& face : vertexToFaceIndex[vertex]) { - vertexToNormal[vertex] += faceToNormal[face]; + + for (const auto& entry : faceNormals) { + const glm::vec3& position = entry.first; + const std::vector& normals = entry.second; + + // Average the face normals for each position + glm::vec3 averagedNormal = glm::vec3(0.0f); + for (const glm::vec3& normal : normals) { + averagedNormal += normal; } - vertexToNormal[vertex] /= vertexToFaceIndex[vertex].size(); - vertexToNormal[vertex] = glm::normalize(vertexToNormal[vertex]); + averagedNormal = glm::normalize(averagedNormal); + + // Map the averaged normal back to the vertices + vertexToNormalMap[position] = averagedNormal; + } + + for (size_t i = 0; i < vertices.size(); ++i) { + vertexToNormal[i] = vertexToNormalMap[vertices[i].position]; } return vertexToNormal;