-
Notifications
You must be signed in to change notification settings - Fork 0
Barycentric coordinates
Barycentric coordinates are a way to express any point inside or outside a triangle as a weighted combination of the triangle three vertices.
For a triangle with vertices A, B, and C, any point P can be expressed as:
P = W0 · A + W1 · B + W2 · C
Where:
-
W0, W1, W2are the barycentric coordinates (weights) -
W0 + W1 + W2 = 1(the weights always sum to 1)
The resulting weights will also be useful for several future operations like texture wrapping.
During the rendering, each pixels of the portion where the triangle is located on the canvas need to be iterated. To determine the portion, the triangle bounding box calculated in the previous step.
// calculate bounding box
const Geometry::Rect bbox = triangle.GetBoundingRect();
// clamp to screen bounds
const std::size_t x0 = (std::size_t)std::max(0.0f, std::floorf(bbox.m_Min.m_X));
const std::size_t x1 = (std::size_t)std::min((float)(m_Width), std::floorf(bbox.m_Max.m_X));
const std::size_t y0 = (std::size_t)std::max(0.0f, std::floorf(bbox.m_Min.m_Y));
const std::size_t y1 = (std::size_t)std::min((float)(m_Height), std::floorf(bbox.m_Max.m_Y));
// rasterize triangle
for (std::size_t y = y0; y <= y1; ++y)
for (std::size_t x = x0; x <= x1; ++x)
{
const Math::Vector2F pixelSample(x + 0.5f, y + 0.5f);
Geometry::Triangle::IWeights weights;
if (triangle.BarycentricInside(pixelSample, weights))
{
...
}
For each iterated pixel, the barycentric coordinates are calculated:
float CalculateSignedArea(const Math::Vector2F& v1,
const Math::Vector2F& v2,
const Math::Vector2F& v3)
{
return (v2.m_X - v1.m_X) * (v3.m_Y - v1.m_Y) - (v2.m_Y - v1.m_Y) * (v3.m_X - v1.m_X);
}
bool BarycentricInside(const Math::Vector2F& point, IWeights& weights) const
{
// whole triangle area
const float areaABC = CalculateSignedArea(m_Vertex[0], m_Vertex[1], m_Vertex[2]);
// sub-triangles areas
const float areaPBC = CalculateSignedArea(point, m_Vertex[1], m_Vertex[2]);
const float areaAPC = CalculateSignedArea(m_Vertex[0], point, m_Vertex[2]);
const float areaABP = CalculateSignedArea(m_Vertex[0], m_Vertex[1], point);
// barycentric coordinates
weights.m_W0 = areaPBC / areaABC;
weights.m_W1 = areaAPC / areaABC;
weights.m_W2 = areaABP / areaABC;
return (weights.m_W0 >= 0 && weights.m_W1 >= 0 && weights.m_W2 >= 0);
}
The BarycentricInside() function will return true if the point is located inside the triangle. If it is located outside, it will be discarded.
There is an available demo which shows explicitly how the barycentric coordinates are calculated inside a triangle.