⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mathex.cpp

📁 常用计算机图形学算法.包括射线,球,直线,与轴平行包围盒相交计算,常用矩阵运算等.
💻 CPP
📖 第 1 页 / 共 2 页
字号:
        //At^2+Bt+C=0,其中A=u*u, B=2(u*(p0-c)),
        //c=(p0-c)*(p0-c)-r*r.如果U是单位向量,A=1;
        //若U归一化,则:t0=(-B+sqrt(B*B-4C))/2; t1=(-B-sqrt(B*B-4C))/2;

        const Vector3r& u = ray.GetDirection();
        const Vector3r  p0_c = ray.GetOrigin() - sphere.GetCenter();
        Real r = sphere.GetRadius();

        Real A = u.Dot(u);
        Real B = 2 * p0_c.Dot(p0_c);
        Real C = p0_c.Dot(p0_c) - r*r;

        //二次方程判别式
        Real D = (B*B)-(4*A*C);
        if (D < 0)
        {
            //如果方程无解,则表示没有交点
            return std::pair<bool, Real>(false, 0);
        }
        else
        {
            //去最终结果.结果会有两个,因为求的是最近的
            //交点这里总是取最小值
            Real t = ( -B - Math::Sqrt(D) ) / (2 * A);
            if (t < 0)
            {
                t = ( -B + Math::Sqrt(D) ) / (2 * A);
            }
            return std::pair<bool, Real>(t>=0, t);
        }
    }
    //-----------------------------------------------------------------------
    std::pair<bool, Real> MathEx::Intersect(const Ray2D& ray, const AxisAlignedRect& rect)
    {
        bool ishit = false;
        Real t;
        Real mint = 0.0f;
        Vector2r hitpoint;
        const Vector2r  min = rect.GetLeftBottom();
        const Vector2r  max = rect.GetRightTop();
        const Vector2r& rayorig = ray.GetOrigin();
        const Vector2r& raydir = ray.GetDirection();

        //先检查射线原点是否在矩形中
        if( rect.Inside(rayorig) )
        {
            return std::pair<bool, Real>(true, 0);
        }

        //再轮流检查4条边
        //如果射线原点在矩形左边并方向向右
        if (rayorig.x < min.x && raydir.x > 0)
        {
            //计算射线到达矩形左边的距离
            t = (min.x - rayorig.x) / raydir.x;
            if(t>0)
            {
                //计算交点
                hitpoint = ray.GetPoint(t);
                //如果交点在矩形的左边的范围内
                if ( hitpoint.y >= min.y && hitpoint.y <= max.y
                && (!ishit || t < mint) )
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在矩形右边并方向向左
        if (rayorig.x > max.x && raydir.x < 0)
        {
            //计算射线到达矩形右边的距离
            t = (max.x - rayorig.x) / raydir.x;
            if(t>0)
            {
                hitpoint = ray.GetPoint(t);
                if ( hitpoint.y >= min.y && hitpoint.y <= max.y
                && (!ishit || t < mint) )
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在矩形下边并方向向上
        if (rayorig.y < min.y && raydir.y > 0)
        {
            //计算射线到达矩形下边平面的距离
            t = (min.y - rayorig.y) / raydir.y;
            if (t>0)
            {
                hitpoint = ray.GetPoint(t);
                if (hitpoint.x >= min.x && hitpoint.x <= max.x
                && (!ishit || t < mint) )
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在矩形上边并方向向下
        if (rayorig.y > max.y && raydir.y < 0)
        {
            //计算射线到达矩形上边平面的距离
            t = (max.y - rayorig.y) / raydir.y;
            if (t>0)
            {
                hitpoint = ray.GetPoint(t);
                if (hitpoint.x >= min.x && hitpoint.x <= max.x 
                && (!ishit || t < mint))
                {
                    ishit = true;
                    mint = t;
                }
            }
        }

        return std::pair<bool, Real>(ishit, mint);
    }
    //-----------------------------------------------------------------------
    std::pair<bool, Real> MathEx::Intersect(const Ray3D& ray, const AxisAlignedBox& box)
    {
        bool ishit = false;
        Real t;
        Real mint = 0.0f;
        Vector3r hitpoint;
        const Vector3r  min = box.GetLeftBottomNear();
        const Vector3r  max = box.GetRightTopFar();
        const Vector3r& rayorig = ray.GetOrigin();
        const Vector3r& raydir = ray.GetDirection();

        //先检查射线原点是否在盒子中
        if( box.Inside( rayorig ) )
        {
            return std::pair<bool, Real>(true, 0);
        }

        //轮流检查6个面
        //如果射线原点在盒子左边并方向向右
        if (rayorig.x < min.x && raydir.x > 0)
        {
            //计算射线到达盒子左边平面的距离
            t = (min.x - rayorig.x) / raydir.x;
            if(t>0)
            {
                //计算交点
                hitpoint = ray.GetPoint(t);
                //如果交点在矩形的左表面范围内
                if ( hitpoint.y >= min.y && hitpoint.y <= max.y
                  && hitpoint.z >= min.z && hitpoint.z <= max.z
                  && (!ishit || t < mint) )
                {
                    //总是将最小的t保存下来
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在盒子的右边并方向向左
        if (rayorig.x > max.x && raydir.x < 0)
        {
            //计算射线到达盒子右边平面的距离
            t = (max.x - rayorig.x) / raydir.x;
            if(t>0)
            {
                hitpoint = ray.GetPoint(t);
                if( hitpoint.y >= min.y && hitpoint.y <= max.y
                 && hitpoint.z >= min.z && hitpoint.z <= max.z
                 && (!ishit || t < mint) )
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在盒子下边并方向向上
        if (rayorig.y < min.y && raydir.y > 0)
        {
            //计算射线到达盒子下边平面的距离
            t = (min.y - rayorig.y) / raydir.y;
            if (t>0)
            {
                hitpoint = ray.GetPoint(t);
                if (hitpoint.x >= min.x && hitpoint.x <= max.x
                 && hitpoint.z >= min.z && hitpoint.z <= max.z
                 && (!ishit || t < mint) )
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在盒子上边并方向向下
        if (rayorig.y > max.y && raydir.y < 0)
        {
            //计算射线到达盒子上边平面的距离
            t = (max.y - rayorig.y) / raydir.y;
            if (t>0)
            {
                hitpoint = ray.GetPoint(t);
                if (hitpoint.x >= min.x && hitpoint.x <= max.x 
                 && hitpoint.z >= min.z && hitpoint.z <= max.z 
                 && (!ishit || t < mint))
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在盒子前边并方向向后
        if (rayorig.z < min.z && raydir.z > 0)
        {
            //计算射线到达盒子前边平面的距离
            t = (min.z - rayorig.z) / raydir.z;
            if (t>0)
            {
                hitpoint = ray.GetPoint(t);
                if (hitpoint.x >= min.x && hitpoint.x <= max.x
                 && hitpoint.y >= min.y && hitpoint.y <= max.y 
                 && (!ishit || t < mint))
                {
                    ishit = true;
                    mint = t;
                }
            }
        }
        //如果射线原点在盒子后边并方向向前
        if (rayorig.z > max.z && raydir.z < 0)
        {
            //计算射线到达盒子后边平面的距离
            t = (max.z - rayorig.z) / raydir.z;
            if (t>0)
            {
                hitpoint = ray.GetPoint(t);
                if (hitpoint.x >= min.x && hitpoint.x <= max.x 
                 && hitpoint.y >= min.y && hitpoint.y <= max.y 
                 && (!ishit || t < mint))
                {
                    ishit = true;
                    mint = t;
                }
            }
        }

        return std::pair<bool, Real>(ishit, mint);
    }
    //-----------------------------------------------------------------------
    bool MathEx::Intersect(const Ray2D& ray, const Vector2r& a,
                           const Vector2r& b, const Vector2r& c)
    {
        //循环判断射线是否与三角形的某条边相交
        if( Intersect(ray, a, b) 
         || Intersect(ray, b, c) 
         || Intersect(ray, a, c) )
        {
            return true;
        }
        return false;
    }
    //-----------------------------------------------------------------------
    std::pair<bool, Real> MathEx::Intersect(const Ray3D& ray, const Vector3r& a,
                                            const Vector3r& b, const Vector3r& c,
                                            bool positiveSide, bool negativeSide)
    {
        //先计算射线与平面的交点,在判断此交点是否在三角形中
        Plane plane(a, b, c);
        return Intersect(ray, a, b, c, plane, positiveSide, negativeSide);
    }
    //-----------------------------------------------------------------------
    std::pair<bool, Real> MathEx::Intersect(const Ray3D& ray, const Vector3r& a,
                                            const Vector3r& b, const Vector3r& c, const Vector3r& normal,
                                            bool positiveSide, bool negativeSide)
    {
        //先计算射线与平面的交点,在判断此交点是否在三角形中
        Plane plane(normal, a);
        return Intersect(ray, a, b, c, plane, positiveSide, negativeSide);
    }
    //-----------------------------------------------------------------------
    std::pair<bool, Real> MathEx::Intersect(const Ray3D& ray, const Vector3r& a,
                                            const Vector3r& b, const Vector3r& c, const Plane& plane,
                                            bool positiveSide, bool negativeSide)
    {
        //计算平面与射线的交点
        std::pair<RayPlaneRelation, Real> instp = Intersect( ray, plane, positiveSide, negativeSide );
        //如果不相交或共面
        if( RPR_NONINTERSECT == instp.first || RPR_COPLANARITY == instp.first )
        {
            return std::pair<bool, Real>(false, 0);
        }
        //判断此交点是否在三角形中
        bool isIn = PointInTri( ray.GetPoint(instp.second), a, b, c, plane.GetNormal(), true );
        return std::pair<bool, Real>(isIn, instp.second);
    }
    //-----------------------------------------------------------------------
    Matrix4r MathEx::CalculateViewMatrix(const Vector3r& eye, const Vector3r& at, const Vector3r& up)
    {
        //在D3D的帮助文档中如是说:
        //zaxis = normal(At - Eye)
        //xaxis = normal(cross(Up, zaxis))
        //yaxis = cross(zaxis, xaxis)
        //
        // xaxis.x           yaxis.x           zaxis.x          0
        // xaxis.y           yaxis.y           zaxis.y          0
        // xaxis.z           yaxis.z           zaxis.z          0
        //-dot(xaxis, eye)  -dot(yaxis, eye)  -dot(zaxis, eye)  l
        //当然,D3D是行主矩阵,而我们是列主矩阵,所以结果是它的转置

        Vector3r zaxis = at-eye;
        zaxis.Normalise();
        Vector3r xaxis = up.Cross(zaxis);
        xaxis.Normalise();
        Vector3r yaxis = zaxis.Cross(xaxis);

        return Matrix4r( xaxis.x, xaxis.y, xaxis.z, -xaxis.Dot(eye),
                         yaxis.x, yaxis.y, yaxis.z, -yaxis.Dot(eye),
                         zaxis.x, zaxis.y, zaxis.z, -zaxis.Dot(eye),
                               0,       0,       0,              1 );
    }
    //-----------------------------------------------------------------------
    Matrix4r MathEx::CalculateProjectionMatrix(Radian fovY, Real aspect, Real zn, Real zf)
    {
		//同样D3D中对投影矩阵的说法:
		//xScale     0          0               0
        //0        yScale       0               0
        //0          0       zf/(zf-zn)         1
        //0          0       -zn*zf/(zf-zn)     0
        //where:
        // yScale = cot(fovY/2)
        // xScale = aspect ratio / yScale
        //当然同样的,我们需要把它转置一下


        Real yScale = Math::Cot( fovY.RadianValue()/2 );
        Real xScale = aspect / yScale;
        Real Q = zf/(zf - zn);
     
        Matrix4r ret = Matrix4r::ZERO;
     
        ret[0][0] = xScale;
        ret[1][1] = yScale;
        ret[2][2] = Q;
        ret[2][3] = -Q*zn;
        ret[3][2] = 1;
        return ret;
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -