cloudy-raytracer/primitive/triangle.cpp

122 lines
5.1 KiB
C++
Raw Normal View History

2022-10-28 09:31:13 +02:00
#include "primitive/triangle.h"
#include <algorithm>
// Constructor /////////////////////////////////////////////////////////////////
Triangle::Triangle(std::shared_ptr<Shader> const &shader) : Primitive(shader) {}
2022-11-11 14:27:43 +01:00
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr<Shader> const &shader) : Primitive(shader), vertex{a, b, c} {}
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, std::shared_ptr<Shader> const &shader) : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc} {}
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
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<Shader> const &shader)
2022-10-28 09:31:13 +02:00
: Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, surface{ta, tb, tc} {}
2022-11-11 14:27:43 +01:00
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<Shader> const &shader)
: Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, tangent{tana, tanb, tanc}, bitangent{ba, bb, bc}, surface{ta, tb, tc} {}
2022-10-28 09:31:13 +02:00
// Primitive functions /////////////////////////////////////////////////////////
2022-11-11 14:27:43 +01:00
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;
}
};
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
// begin ray-plane intersection ----------------------------
Vector3d normal = normalized(crossProduct(vertex[2] - vertex[0], vertex[1] - vertex[0]));
float const cosine = dotProduct(ray.direction, normal);
2022-11-17 16:16:32 +01:00
if (std::abs(cosine) < EPSILON)
2022-11-11 14:27:43 +01:00
return false;
float const t = dotProduct(vertex[0] - ray.origin, normal) / cosine;
if (t < EPSILON || ray.length < t)
return false;
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;
if ((a < 0.0f) || (a > 1.0f) || (b < 0.0f) || (a + b > 1.0f))
return false;
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
// 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
2022-11-17 16:16:32 +01:00
ray.normal = normal;
2022-11-11 14:27:43 +01:00
ray.length = t;
ray.primitive = this;
// True, because the primitive was hit
return true;
}
bool Triangle::intersect(Ray &ray) const {
// We use the MöllerTrumbore 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];
// 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 (fabs(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 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;
2022-10-28 09:31:13 +02:00
// Test whether this is the foremost primitive in front of the camera
2022-11-11 14:27:43 +01:00
float const t = dotProduct(edge2, qVec) * inv_det;
if (t < EPSILON || ray.length < t)
return false;
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
// Calculate the normal
2022-11-17 19:22:30 +01:00
ray.normal = normalized(crossProduct(edge1, edge1));
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
// Calculate the surface position
ray.surface = u * this->surface[1] + v * this->surface[2] + (1 - u - v) * this->surface[0];
2022-10-28 09:31:13 +02:00
// Set the new length and the current primitive
2022-11-11 14:27:43 +01:00
ray.length = t;
ray.primitive = this;
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
// True, because the primitive was hit
return true;
2022-10-28 09:31:13 +02:00
}
// Bounding box ////////////////////////////////////////////////////////////////
2022-11-11 14:27:43 +01:00
float Triangle::minimumBounds(int dimension) const { return std::min(this->vertex[0][dimension], std::min(this->vertex[1][dimension], this->vertex[2][dimension])); }
2022-10-28 09:31:13 +02:00
2022-11-11 14:27:43 +01:00
float Triangle::maximumBounds(int dimension) const { return std::max(this->vertex[0][dimension], std::max(this->vertex[1][dimension], this->vertex[2][dimension])); }