cloudy-raytracer/primitive/triangle.cpp
2022-11-25 13:22:14 +01:00

146 lines
5.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "primitive/triangle.h"
#include <algorithm>
#include <iostream>
// Constructor /////////////////////////////////////////////////////////////////
Triangle::Triangle(std::shared_ptr<Shader> const &shader) : Primitive(shader) {}
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr<Shader> 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<Shader> 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<Shader> 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<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} {}
// 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;
}
};
// begin ray-plane intersection ----------------------------
Vector3d normal = normalized(crossProduct(vertex[2] - vertex[0], vertex[1] - vertex[0]));
float const cosine = dotProduct(ray.direction, normal);
if (std::abs(cosine) < EPSILON)
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;
// 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;
// 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 (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 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;
// 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];
// Set the new length and the current primitive
ray.length = t;
ray.primitive = this;
// 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::maximumBounds(int dimension) const {
return std::max(this->vertex[0][dimension],
std::max(this->vertex[1][dimension],
this->vertex[2][dimension]));
}