-
Notifications
You must be signed in to change notification settings - Fork 0
Description
First of all, thanks for the useful educational piece of code!
Sorry if resurrecting dead code, but I decided to notify you of an issue I had while using your clipping algorithm in my personal project.
You seem to perform clipping in NDC space (after perspective division by w), which collapses all Z data to the front of the camera. So essentially your clipping along Z isn't doing anything, and geometry that is behind the camera might appear in front at specific angles. I haven't really built and tested the demo, but I suspect you have no cases where geometry could position itself on the back of the camera, so you haven't noticed it.
Here's a modified clipping algorithm that could be useful to fix the issue, if you decide its worth it:
/// Credit: https://github.com/Gaukler/Software-Rasterizer
/// However the mentioned code clips in NDC space, which presumably works
/// only if there's no chance at anything getting behind the camera.
/// I modified the code, so that it works in clipspace.
template<int AXIS>
std::vector<Vec4> ClipLine(const std::vector<Vec4>& vertices) {
auto isInside = [](const Vec4& p) {
return p[AXIS] > -p.w and p[AXIS] < p.w;
};
std::vector<Vec4> clipped;
for (size_t i = 0; i < vertices.size(); i++) {
Vec4 v1 = vertices[i];
Vec4 v2 = vertices[(i + 1) % vertices.size()];
if (isInside(v1) and isInside(v2)) {
// Both points in
clipped.emplace_back(v2);
}
else if (not isInside(v1) and not isInside(v2)) {
// Both points out, nothing to draw
}
else if (v1[AXIS] > v1.w) {
// Mixed
auto t = (v1[AXIS] - v1.w) / ((v1[AXIS] - v1.w) - (v2[AXIS] - v2.w));
clipped.emplace_back(t * v2 + (1 - t) * v1);
clipped.emplace_back(v2);
}
else if (v1[AXIS] < -v1.w) {
// Mixed
auto t = (v1[AXIS] + v1.w) / ((v1[AXIS] + v1.w) - (v2[AXIS] + v2.w));
clipped.emplace_back(t * v2 + (1 - t) * v1);
clipped.emplace_back(v2);
}
else if (v2[AXIS] > v2.w) {
// Mixed
auto t = (v2[AXIS] - v2.w) / ((v2[AXIS] - v2.w) - (v1[AXIS] - v1.w));
clipped.emplace_back(t * v1 + (1 - t) * v2);
}
else if (v2[AXIS] < -v2.w) {
// Mixed
auto t = (v2[AXIS] + v2.w) / ((v2[AXIS] + v2.w) - (v1[AXIS] + v1.w));
clipped.emplace_back(t * v1 + (1 - t) * v2);
}
}
return clipped;
}
/// Clip a triangle depending on how many vertices are in viewport
/// @param MVP - model*view*projection matrix
/// @param triangle - the triangle to clip
/// @param rasterizer - rasterizer to use
void ASCIIPipeline::ClipTriangle(
const Mat4& MVP, const ASCIIGeometry::Vertex* triangle, auto&& rasterizer
) const {
// Transform to viewspace
std::vector<Vec4> points;
points.emplace_back(MVP * triangle[0].mPos);
points.emplace_back(MVP * triangle[1].mPos);
points.emplace_back(MVP * triangle[2].mPos);
// Clip
//points = ClipLine<0>(points);
//points = ClipLine<1>(points);
points = ClipLine<2>(points); // Clipping only z is enough for me,
// but also clipping along x and y
// produces undesired artifacts
if (points.size() < 3)
return;
// Do perspective division (collapses -Z onto +Z)
for (auto& p : points)
p /= p.w;
// Create a triangle fan
for (size_t i = 1; i < points.size() - 1; ++i)
rasterizer(Triangle4 {points[0], points[i], points[i + 1]});
}All the best!