zoukankan      html  css  js  c++  java
  • 40. D3D平面

    平面是一个无限扩展的区域。读者可以将其形象化为沿着上下两个方向无限扩展的平面墙。如果很难理解平面这个概念,那么试着考虑一下宇宙。想象一样,宇宙非常平而且沿着所有其他边不断无限扩展的情况。

           虽然屏幕区域有限,但可以在屏幕上绘制平面,因为只要对有限数量的像素做阴影处理即可。然而,在Direct3D和OpenGL中并没有内置渲染平面的方法。所以,如果不能渲染平面,那就会出现问题,即平面到底对谁有好处?关于这个问题,答案有很多。最常见的一个答案就是几何图形选择和碰撞检测

    有了几何图形选择,就可以创建一个平面或一系列平面,而这取决于几何图形位于平面的哪一侧,并可以快速确定是否需要绘制该对象。因为这可以很大程度地加速程序的渲染次数,所以这种方法很有用。使用选择法通过几个快速数学运算,就可以快速拒绝将要发送给渲染API的几何图形。如果场景中有数十、数百或是更多的浏览者不可见的对象,那么使用选择法就可以节省大量的时间和处理操作。另一方面,如果真正要渲染场景中的每个几何图形,而不考虑它是否可见,那就会注意到程序帧数显著下降。下面的公式是从数学上定义一个平面:
    V*N + D = 0

           方程中的D代表平面到原点的距离。变量V代表位于平面某个位置上的点。该点可以在平面的任意位置上。变量N是平面法线。平面法线是与平面正交的矢量,它朝向平面前面的方向。利用该信息可以确定平面的哪一侧是前面,哪一侧是后面。这样就可以测试一个点或其他几何图形是在平面前面、后面或是在平面扩展处。如果某个视角周围有6个平面,分别代表左、右、上、下、远、近,那么就可以描述对象可见、不可见或部分可见。此时,可以拒绝或渲染测试后的几何图形。对大场景而言,这样做可以节省大量的时间。

     平面类中的成员变量并不多,所需的全部成员变量就是平面的法线(由x、y、z变成了a、b、c)和平面距离。这意味着屏幕结构主要由4个浮点变量a、b、c、d构成。

    class CPlane
    {
    public:
    CPlane();
    CPlane(
    float A, float B, float C, float D);

    void CreatePlaneFromTri(Vector3D &v1, Vector3D &v2,
    Vector3D
    &v3);

    bool Intersect(CPlane &p1, Vector3D *point);
    bool Intersect(CAabb &aabb);

    int ClassifyPolygon(CPolygon &pol);
    int ClassifyPoint(Vector3D &v);

    float GetDistance(Vector3D &v);

    float a, b, c, d;
    };

      

    CPlane::CPlane()
    {
    a
    = 0;
    b
    = 0;
    c
    = 0;
    d
    = 0;
    }


    CPlane::CPlane(
    float A, float B, float C, float D)
    {
    a
    = A;
    b
    = B;
    c
    = C;
    d
    = D;
    }

      平面类中的下一个函数是CreatePlaneFromTri()。该函数利用三角形信息计算平面法线和距离。该函数以构成三角形的三个顶点为参数。CreatePlaneFromTri()函数内部计算三角形的法线,并将三角形的x、y、z成员变量设为平面的a、b、c成员变量。距离d通过计算平面法线和第一个顶点的点积,并对该点积求反得到。CreatePlaneFromTri()函数如程序

    void CPlane::CreatePlaneFromTri(Vector3D &v1, Vector3D &v2, Vector3D &v3)
    {
    Vector3D normal, e1, e2;

    e1
    = v3 - v1;
    e2
    = v2 - v1;

    e1.Normal();
    e2.Normal();

    normal.CrossProduct(e1, e2);
    normal.Normal();

    a
    = normal.x;
    b
    = normal.y;
    c
    = normal.z;
    d
    = - (a * tril.x + b * tril.y + c * tril.z);
    }

      

       平面类中的下一个函数是第一个交叉点函数。Intersect()函数用于测试两个平面之间是否相交。如果这两个平面相交,函数返回true(真)及相交的交叉点。该函数的参数包括第二个平面对象,以及如果要和平面相交将要存储交叉的矢量对象上的点。测试两个平面是否相交的Intersect()函数的完整代码如程序清单

    bool CPlane::Intersect(CPlane &pl, Vector3D *intersectPoint)
    {
    Vector3D cross;
    Vector3D normal(a, b, c);
    Vector3D plNormal(pl.a, pl.b, pl.c);
    float length = 0;

    cross.CrossProduct(normal, plNormal);
    length
    = cross.DotProduct3(cross);

    if(length < 1e-08f) return false;

    if(intersectPoint)
    {
    float l0 = normal.DotProduct3(normal);
    float l1 = normal.DotProduct3(plNormal);
    float l2 = plNormal.DotProduct3(plNormal);
    float det = l0 * l2 - l1 * l1;
    float invDet = 0;

    if(fabs(det) < 1e-08f) return false;

    invDet
    = 1 / det;
    float d0 = (l2 * d - l1 * pl.d) * invDet;
    float d1 = (l0 * pl.d - l1 * d) * invDet;

    (
    *intersectPoint) = normal * d0 + plNormal * d1;
    }

    return true;
    }

      平面类中的另一个交叉函数是重载的Intersect()函数。该函数测试平面是否和轴对称的边界框相交。这将在本书稍后的BSP树演示程序中派上用场。

    bool CPlane::Intersect(CAabb &aabb)
    {
    Vector3D min, max;
    Vector3D normal(a, b, c);

    if (normal.x >= 0.0f)
    {
    min.x
    = aabb.m_min.x;
    max.x
    = aabb.m_max.x;
    }
    else
    {
    min.x
    = aabb.m_max.x;
    max.x
    = aabb.m_min.x;
    }

    if (normal.y >= 0.0f)
    {
    min.y
    = aabb.m_min.y;
    max.y
    = aabb.m_max.y;
    }
    else
    {
    min.y
    = aabb.m_max.y;
    max.y
    = aabb.m_min.y;
    }

    if (normal.z >= 0.0f)
    {
    min.z
    = aabb.m_min.z;
    max.z
    = aabb.m_max.z;
    }
    else
    {
    min.z
    = aabb.m_max.z;
    max.z
    = aabb.m_min.z;
    }

    if ((normal.DotProduct3(min)+d) > 0.0f) return false;
    if ((normal.DotProduct3(max)+d) >= 0.0f) return true;

    return false;
    }

         对平面而言,要知道点位于平面哪一侧。点可以在平面上,也可以在平面前面,还可以在平面后面。通常在不同的选择算法中会使用它,而且还可用它确定是否要绘制某些内容。同样可以在碰撞时使用它。如果旧位置位于平面前面,而新位置位于平面后面,那么因为位置穿过平面,所以可知发生了碰撞。确定点位于平面哪一侧的方法称为“分类点”。在平面类中有一个名为ClassifyPoint()的函数。ClassifyPoint()函数如程序清单8.25所示,它以3D位置为参数。该函数通过计算平面和位置的点积,并加上平面距离而返回点和平面的相对位置,点要么在平面前面、要么在平面后面,要么在平面上。如果距离小于0,表示点在平面后面;大于0,表示在平面前面;等于0,则在平面上。

    int CPlane::ClassifyPoint(Vector3D &v)
    {
    float distance = a * v.x + b * v.y + c * v.z + d;

    if(distance > 0.001) return UGP_FRONT;
    if(distance < -0.001) return UGP_BACK;

    return UGP_ON_PLANE;
    }

      了解了点分类的方法就可以确认三角形或多边形位于平面哪一侧。这通过将图元中的所有点分类加以确认。如果所有的点都在多边形的一侧,那么整个图元就在该多边形的那一侧。如果某些点在多边形一侧,某些点在另一侧,那么该图元穿过平面。程序清单8.26中的ClassifyPolygon()函数完成测试。它以一个多边形对象(稍后将会是更多的多边形对象)为参数,并返回多边形是在平面的前面、后面还是穿过该平面的结果。

    int CPlane::ClassifyPolygon(CPolygon &pol)
    {
    int frontPolys = 0;
    int backPolys = 0;
    int planePolys = 0;
    int type = 0;

    for(int i = 0; i < 3; i++)
    {
    type
    = ClassifyPoint(pol.m_vertexList[i]);

    switch(type)
    {
    case UGP_FRONT:
    frontPolys
    ++;
    break;

    case UGP_BACK:
    backPolys
    ++;
    break;

    default:
    frontPolys
    ++;
    backPolys
    ++;
    planePolys
    ++;
    break;
    }
    }

    if(planePolys == 3) return UGP_ON_PLANE;
    else if(frontPolys == 3) return UGP_FRONT;
    else if(backPolys == 3) return UGP_BACK;

    return UGP_CLIPPED;
    }

       平面类的最后一个函数计算平面到一个3D矢量的距离。计算平面和3D矢量之间的点积可以得到该距离,返回结果为一浮点值。程序清单8.27给出了平面GetDistance()成员函数的实现代码。

    float CPlane::GetDistance(Vector3D &v)
    {
    return a*v.x + b*v.y + c*v.z + d;
    }

      Direct3D平面

           如同所有其他数学结构一样,Direct3D还包含了一套平面对象。Direct3D平面对象D3DXPLANE包含了计算平面的4D矢量点积的D3DXPlaneDot()函数、计算平面和3D矢量点积的D3DXPlaneDotCoord()函数、使用假定w为0计算平面和3D矢量点积的D3DXPlaneDotCoord()函数、由三角形计算平面的D3DXPlaneFromPoints()函数及由点和法线计算平面的D3DXPlaneFromPointNormal()函数。如果准备使用Direct3D平面,那么可以查看DirectX SDK文档获取完整的信息。D3DXPLANE结构如程序清单8.28所示。

    typedef struct D3DXPLANE {
    FLOAT a;
    FLOAT b;
    FLOAT c;
    FLOAT d;
    } D3DXPLANE;

      

  • 相关阅读:
    实验二Step1-有序顺序表
    0330复利计算4.0(改)
    0330复利计算4.0单元测试
    实验一 命令解释程序的编写
    《构建之法》之第1、2、3章读后感
    0408-汉堡包的方式评价合作伙伴
    0406-复利计算5.0
    0405—软件工程 《构建之法》 第四章读后感
    03-29复利计算单元测试
    03-25实验一、命令解释程序的编写
  • 原文地址:https://www.cnblogs.com/kex1n/p/2173585.html
Copyright © 2011-2022 走看看