From 31b17da22170d8983c660335e7a9d8f47aea84f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?arvid=20schr=C3=B6der?= Date: Fri, 25 Nov 2022 13:20:05 +0100 Subject: [PATCH] added advanced normal calculation --- primitive/triangle.cpp | 181 +++++++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 78 deletions(-) diff --git a/primitive/triangle.cpp b/primitive/triangle.cpp index 416233d..c6538d2 100644 --- a/primitive/triangle.cpp +++ b/primitive/triangle.cpp @@ -1,121 +1,146 @@ #include "primitive/triangle.h" #include +#include // Constructor ///////////////////////////////////////////////////////////////// Triangle::Triangle(std::shared_ptr const &shader) : Primitive(shader) {} -Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr const &shader) : Primitive(shader), vertex{a, b, c} {} +Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr const &shader) + : Primitive(shader), vertex{a, b, c} {} -Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, std::shared_ptr const &shader) : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc} {} +Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, + Vector3d const &nc, std::shared_ptr const &shader) : Primitive(shader), vertex{a, b, c}, + normal{na, nb, nc} {} -Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc, std::shared_ptr const &shader) - : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, surface{ta, tb, tc} {} +Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, + Vector3d const &nc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc, + std::shared_ptr const &shader) + : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, surface{ta, tb, tc} {} -Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, Vector3d const &tana, Vector3d const &tanb, Vector3d const &tanc, Vector3d const &ba, - Vector3d const &bb, Vector3d const &bc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc, std::shared_ptr const &shader) - : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, tangent{tana, tanb, tanc}, bitangent{ba, bb, bc}, surface{ta, tb, tc} {} +Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, + Vector3d const &nc, Vector3d const &tana, Vector3d const &tanb, Vector3d const &tanc, + Vector3d const &ba, + Vector3d const &bb, Vector3d const &bc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc, + std::shared_ptr const &shader) + : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, tangent{tana, tanb, tanc}, bitangent{ba, bb, bc}, + surface{ta, tb, tc} {} // Primitive functions ///////////////////////////////////////////////////////// bool Triangle::intersectArea(Ray &ray) const { - // alternative triangle test - // "signed" triangle area with respect to triangle normal - auto triangleArea = [](Vector3d const &v0, Vector3d const &v1, Vector3d const &v2, Vector3d const &normal = Vector3d(0, 0, 0)) { - if (length(normal) < EPSILON) { - return length(crossProduct(v2 - v0, v1 - v0)) / 2.0f; - } else { - Vector3d const cp = crossProduct(v2 - v0, v1 - v0); - return dotProduct(cp, normal) > 0.0f ? length(cp) / 2.0f : -length(cp) / 2.0f; - } - }; + // alternative triangle test + // "signed" triangle area with respect to triangle normal + auto triangleArea = [](Vector3d const &v0, Vector3d const &v1, Vector3d const &v2, + Vector3d const &normal = Vector3d(0, 0, 0)) { + if (length(normal) < EPSILON) { + return length(crossProduct(v2 - v0, v1 - v0)) / 2.0f; + } else { + Vector3d const cp = crossProduct(v2 - v0, v1 - v0); + return dotProduct(cp, normal) > 0.0f ? length(cp) / 2.0f : -length(cp) / 2.0f; + } + }; - // begin ray-plane intersection ---------------------------- - Vector3d normal = normalized(crossProduct(vertex[2] - vertex[0], vertex[1] - vertex[0])); + // begin ray-plane intersection ---------------------------- + Vector3d normal = normalized(crossProduct(vertex[2] - vertex[0], vertex[1] - vertex[0])); - float const cosine = dotProduct(ray.direction, normal); + float const cosine = dotProduct(ray.direction, normal); - if (std::abs(cosine) < EPSILON) - return false; + if (std::abs(cosine) < EPSILON) + return false; - float const t = dotProduct(vertex[0] - ray.origin, normal) / cosine; + float const t = dotProduct(vertex[0] - ray.origin, normal) / cosine; - if (t < EPSILON || ray.length < t) - return false; + if (t < EPSILON || ray.length < t) + return false; - Vector3d const p = ray.origin + t * ray.direction; - // end ray-plane intersection ---------------------------- + Vector3d const p = ray.origin + t * ray.direction; + // end ray-plane intersection ---------------------------- - float const fullArea = triangleArea(vertex[0], vertex[1], vertex[2]); - float const a = triangleArea(p, vertex[0], vertex[1], normal) / fullArea; - float const b = triangleArea(p, vertex[2], vertex[0], normal) / fullArea; + float const fullArea = triangleArea(vertex[0], vertex[1], vertex[2]); + float const a = triangleArea(p, vertex[0], vertex[1], normal) / fullArea; + float const b = triangleArea(p, vertex[2], vertex[0], normal) / fullArea; - if ((a < 0.0f) || (a > 1.0f) || (b < 0.0f) || (a + b > 1.0f)) - return false; + if ((a < 0.0f) || (a > 1.0f) || (b < 0.0f) || (a + b > 1.0f)) + return false; - // Set the surface position (barycentric coordinates) and tangent Vector - ray.surface = a * this->surface[1] + b * this->surface[2] + (1 - a - b) * this->surface[0]; + // Set the surface position (barycentric coordinates) and tangent Vector + ray.surface = a * this->surface[1] + b * this->surface[2] + (1 - a - b) * this->surface[0]; - // Set the new length and the current primitive - ray.normal = normal; - ray.length = t; - ray.primitive = this; + // Set the new length and the current primitive + ray.normal = normal; + ray.length = t; + ray.primitive = this; - // True, because the primitive was hit - return true; + // True, because the primitive was hit + return true; } bool Triangle::intersect(Ray &ray) const { - // We use the Möller–Trumbore intersection algorithm + // We use the Möller–Trumbore intersection algorithm - // Determine two neighboring edge vectors - Vector3d const edge1 = this->vertex[1] - this->vertex[0]; - Vector3d const edge2 = this->vertex[2] - this->vertex[0]; + // Determine two neighboring edge vectors + Vector3d const edge1 = this->vertex[1] - this->vertex[0]; + Vector3d const edge2 = this->vertex[2] - this->vertex[0]; - // Begin calculating determinant - Vector3d const pVec = crossProduct(ray.direction, edge2); + // Begin calculating determinant + Vector3d const pVec = crossProduct(ray.direction, edge2); - // Make sure the ray is not parallel to the triangle - float const det = dotProduct(edge1, pVec); - if (std::abs(det) < EPSILON) - return false; - float const inv_det = 1.0f / det; + // Make sure the ray is not parallel to the triangle + float const det = dotProduct(edge1, pVec); + if (std::abs(det) < EPSILON) + return false; + float const inv_det = 1.0f / det; - // Calculate u and test bound - Vector3d const tVec = ray.origin - this->vertex[0]; - float const u = dotProduct(tVec, pVec) * inv_det; - // Test whether the intersection lies outside the triangle - if (0.0f > u || u > 1.0f) - return false; + // Calculate u and test bound + Vector3d const tVec = ray.origin - this->vertex[0]; + float const u = dotProduct(tVec, pVec) * inv_det; + // Test whether the intersection lies outside the triangle + if (0.0f > u || u > 1.0f) + return false; - // Calculate v and test bound - Vector3d const qVec = crossProduct(tVec, edge1); - float const v = dotProduct(ray.direction, qVec) * inv_det; - // Test whether the intersection lies outside the triangle - if (0.0f > v || u + v > 1.0f) - return false; + // Calculate v and test bound + Vector3d const qVec = crossProduct(tVec, edge1); + float const v = dotProduct(ray.direction, qVec) * inv_det; + // Test whether the intersection lies outside the triangle + if (0.0f > v || u + v > 1.0f) + return false; - // Test whether this is the foremost primitive in front of the camera - float const t = dotProduct(edge2, qVec) * inv_det; - if (t < EPSILON || ray.length < t) - return false; + // Test whether this is the foremost primitive in front of the camera + float const t = dotProduct(edge2, qVec) * inv_det; + if (t < EPSILON || ray.length < t) + return false; - // Calculate the normal - ray.normal = normalized(crossProduct(edge1, edge2)); + // Calculate the normal + if (this->normal[0] == Vector3d(0, 0, 0) + || this->normal[1] == Vector3d(0, 0, 0) + || this->normal[2] == Vector3d(0, 0, 0)) { + ray.normal = normalized(crossProduct(edge1, edge2)); + } else { + ray.normal = normalized(u * normalized(this->normal[1]) + v * normalized(this->normal[2]) + (1 - u - v) * normalized(this->normal[0])); + } - // Calculate the surface position - ray.surface = u * this->surface[1] + v * this->surface[2] + (1 - u - v) * this->surface[0]; + // Calculate the surface position + ray.surface = u * this->surface[1] + v * this->surface[2] + (1 - u - v) * this->surface[0]; - // Set the new length and the current primitive - ray.length = t; - ray.primitive = this; + // Set the new length and the current primitive + ray.length = t; + ray.primitive = this; - // True, because the primitive was hit - return true; + // True, because the primitive was hit + return true; } // Bounding box //////////////////////////////////////////////////////////////// -float Triangle::minimumBounds(int dimension) const { return std::min(this->vertex[0][dimension], std::min(this->vertex[1][dimension], this->vertex[2][dimension])); } +float Triangle::minimumBounds(int dimension) const { + return std::min(this->vertex[0][dimension], + std::min(this->vertex[1][dimension], + this->vertex[2][dimension])); +} -float Triangle::maximumBounds(int dimension) const { return std::max(this->vertex[0][dimension], std::max(this->vertex[1][dimension], this->vertex[2][dimension])); } +float Triangle::maximumBounds(int dimension) const { + return std::max(this->vertex[0][dimension], + std::max(this->vertex[1][dimension], + this->vertex[2][dimension])); +}