📄 mathex.cpp
字号:
//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 + -