裁剪(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;
}

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;
        }
    }
}

效果演示:

===========cotaku的分割线===========

下一篇讲摄像机的实现。

» 转载请注明来源及链接:未来代码研究所

Related Posts:

3 Responses to “PSP图形编程研究手记8:裁剪与背面剔除”

Leave a Reply

World Line
Time Machine
Online Tools