裁剪(clipping)和背面剔除(back-face culling)夹在投影变换和光栅化两步中间,主要负责剔除超出屏幕坐标系的顶点和背向摄像机的三角形,减小渲染压力。裁剪还有另一个重要用途就是剪掉投影变换前z坐标小于0的点,以免出现诡异的结果以及除零错误。
裁剪
一个在屏幕坐标系中的三角形可能出现四种情况:
1、三个顶点都在屏幕内,这种情况下无需裁减。
2、三个顶点都在屏幕外,这种情况下需要将三角形整个剔除。
3、两个顶点在屏幕外、一个顶点在屏幕内,如图:
这种情况比较简单,将顶点a、b分别更新为与屏幕边缘平面的交点A、B即可。
4、一个顶点在屏幕外、两个顶点在屏幕内,如图:
这种情况比较复杂,需要将顶点c变为两个新顶点a、b,并且将原三角形变为两个新的三角形。
计算线段与屏幕边缘平面的交点的方法:
Point3 CalcIntersection(const Point3 p1, const Point3 p2, double w1, double w2, CLIP side)
{
Point3 point;
double x1 = p1.x / w1;
double y1 = p1.y / w1;
double z1 = p1.z / w1;
double x2 = p2.x / w2;
double y2 = p2.y / w2;
double z2 = p2.z / w2;
double t;
switch (side)
{
case HITHER:
{
t = z1 / (z1 - z2);
point.x = x1 + (x2 - x1) * t;
point.y = y1 + (y2 - y1) * t;
point.z = 0;
break;
}
case YON:
{
t = (z1 - 1) / (z1 - z2);
point.x = x1 + (x2 - x1) * t;
point.y = y1 + (y2 - y1) * t;
point.z = 1;
break;
}
case LEFT:
{
t = (x1 + 1) / (x1 - x2);
point.x = -1;
point.y = y1 + (y2 - y1) * t;
point.z = z1 + (z2 - z1) * t;
break;
}
case RIGHT:
{
t = (x1 - 1) / (x1 - x2);
point.x = 1;
point.y = y1 + (y2 - y1) * t;
point.z = z1 + (z2 - z1) * t;
break;
}
case BOTTOM:
{
t = (y1 + 1) / (y1 - y2);
point.x = x1 + (x2 - x1) * t;
point.y = -1;
point.z = z1 + (z2 - z1) * t;
break;
}
case TOP:
{
t = (y1 - 1) / (y1 - y2);
point.x = x1 + (x2 - x1) * t;
point.y = 1;
point.z = z1 + (z2 - z1) * t;
break;
}
}
point.color.R = p1.color.R + (p2.color.R - p1.color.R) * t;
point.color.G = p1.color.G + (p2.color.G - p1.color.G) * t;
point.color.B = p1.color.B + (p2.color.B - p1.color.B) * t;
point.color.A = p1.color.A + (p2.color.A - p1.color.A) * t;
return point;
}
{
Point3 point;
double x1 = p1.x / w1;
double y1 = p1.y / w1;
double z1 = p1.z / w1;
double x2 = p2.x / w2;
double y2 = p2.y / w2;
double z2 = p2.z / w2;
double t;
switch (side)
{
case HITHER:
{
t = z1 / (z1 - z2);
point.x = x1 + (x2 - x1) * t;
point.y = y1 + (y2 - y1) * t;
point.z = 0;
break;
}
case YON:
{
t = (z1 - 1) / (z1 - z2);
point.x = x1 + (x2 - x1) * t;
point.y = y1 + (y2 - y1) * t;
point.z = 1;
break;
}
case LEFT:
{
t = (x1 + 1) / (x1 - x2);
point.x = -1;
point.y = y1 + (y2 - y1) * t;
point.z = z1 + (z2 - z1) * t;
break;
}
case RIGHT:
{
t = (x1 - 1) / (x1 - x2);
point.x = 1;
point.y = y1 + (y2 - y1) * t;
point.z = z1 + (z2 - z1) * t;
break;
}
case BOTTOM:
{
t = (y1 + 1) / (y1 - y2);
point.x = x1 + (x2 - x1) * t;
point.y = -1;
point.z = z1 + (z2 - z1) * t;
break;
}
case TOP:
{
t = (y1 - 1) / (y1 - y2);
point.x = x1 + (x2 - x1) * t;
point.y = 1;
point.z = z1 + (z2 - z1) * t;
break;
}
}
point.color.R = p1.color.R + (p2.color.R - p1.color.R) * t;
point.color.G = p1.color.G + (p2.color.G - p1.color.G) * t;
point.color.B = p1.color.B + (p2.color.B - p1.color.B) * t;
point.color.A = p1.color.A + (p2.color.A - p1.color.A) * t;
return point;
}
Clip函数的基本流程就是遍历所有的三角形,判断该三角形的处理方式,计算交点、有需要的话产生新的三角形,并更新points数组和indices数组。
效果演示(近面裁剪):
背面剔除
原理很简单,将法向量z坐标值小于0的三角形剔除即可:
void Cull(vector<Point3> *points, vector<unsigned int> *indices)
{
Point3 p1, p2, p3;
int iEnd = indices->size();
for (int i = 0; i < iEnd; i += 3)
{
p1 = (*points)[(*indices)[i]];
p2 = (*points)[(*indices)[i + 1]];
p3 = (*points)[(*indices)[i + 2]];
double z = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
if (z < 0)
{
(*indices)[i] = (*indices)[iEnd - 3];
(*indices)[i + 1] = (*indices)[iEnd - 2];
(*indices)[i + 2] = (*indices)[iEnd - 1];
indices->pop_back();
indices->pop_back();
indices->pop_back();
iEnd -= 3;
i -= 3;
}
}
}
{
Point3 p1, p2, p3;
int iEnd = indices->size();
for (int i = 0; i < iEnd; i += 3)
{
p1 = (*points)[(*indices)[i]];
p2 = (*points)[(*indices)[i + 1]];
p3 = (*points)[(*indices)[i + 2]];
double z = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
if (z < 0)
{
(*indices)[i] = (*indices)[iEnd - 3];
(*indices)[i + 1] = (*indices)[iEnd - 2];
(*indices)[i + 2] = (*indices)[iEnd - 1];
indices->pop_back();
indices->pop_back();
indices->pop_back();
iEnd -= 3;
i -= 3;
}
}
}
效果演示:
===========cotaku的分割线===========
下一篇讲摄像机的实现。
» 转载请注明来源及链接:未来代码研究所
how can i get touch with you?there are many questions that i really want to figure them out!
please reply soon
好吧好吧我是因为看到回复栏的英文才以为要用英文回复(我可以理解为你们正文都是有人翻译的吗?)我的QQ4235992.好多东西想请教的,希望能尽快回复吧