路径追踪03 相交性检测

三角形1:

Ps:三角形可拓展至任意网格

推导过程:

射线:R(t)=O+tD  (O原点,D方向,t距离)

三角形:T(u,v)=(1-u-v)V_0+uV_1+vV_2(V123 顶点  uvw 重心坐标)

求相交点 R(t)=T(u,v)

(1-u-v)V_0+uV_1+vV_2=O+tD

u(V_1-V_0)+v(V_2-V_0)+t(-D)=O-V_0

等价于矩阵运算 

\begin{bmatrix} -D & V_1-V_0 &V_2-V_0 \end{bmatrix} \begin{bmatrix} t\\ u \\ v \end{bmatrix} =O-V_0

设V1-V0=E1 V2-V0=E2 O-V0=T

\begin{bmatrix} -D & E_1 &E_2 \end{bmatrix} \begin{bmatrix} t\\ u \\ v \end{bmatrix} =T

根据克莱姆法则,三个值分别为

t=\frac{1}{\begin{vmatrix} -D & E_1 &E_2 \end{vmatrix}}\begin{vmatrix} T & E_1 &E_2 \end{vmatrix}

u=\frac{1}{\begin{vmatrix} -D & E_1 &E_2 \end{vmatrix}}\begin{vmatrix} -D & T&E_2 \end{vmatrix}

v=\frac{1}{\begin{vmatrix} -D & E_1 &E_2 \end{vmatrix}}\begin{vmatrix} -D & E_1 &T \end{vmatrix}

整合在一起就是

\begin{bmatrix} t \\u\\v \end{bmatrix} =\frac{1}{\begin{vmatrix} -D & E_1 &E_2 \end{vmatrix}} \begin{vmatrix} T & E_1 &E_2 \\ -D&T &E_2 \\ -D & E_1&T \end{vmatrix}

根据混合积公式

\begin{bmatrix} t \\u\\v \end{bmatrix} =\frac{1}{(D\times E_2)\cdot E_1} \begin{vmatrix} (T\times E_1)\cdot E_2 \\(D\times E_2)\cdot T \\(T\times E_1)\cdot D \end{vmatrix}

设P=DxE2 Q=TxE1

\begin{bmatrix} t \\u\\v \end{bmatrix} =\frac{1}{P\cdot E_1} \begin{vmatrix} Q\cdot E_2 \\P\cdot T \\Q\cdot D \end{vmatrix}

代码(hlsl):

//三角形的相交性检测 双面 uvw 重心坐标 w=1-u-v  t=距离
static const float EPSILON = 1e-8;
bool IntersectTriangle_01(Ray ray, float3 vert0, float3 vert1, float3 vert2,
    inout float t, inout float u, inout float v,inout float toward)
{
    //E1 E2
    float3 edge1 = vert1 - vert0;
    float3 edge2 = vert2 - vert0;
    //P=DxE2
    float3 pvec = cross(ray.direction, edge2);
    float det = dot(edge1, pvec);
    //背面det<0 正面>0 在三角形内=0
    if (det < EPSILON&&det >-EPSILON) return false;
    toward=sign(det);
    // 1/(DxE2)·E1    
    float inv_det = 1.0f / det;
    //T
    float3 tvec = ray.origin - vert0;
    //u=invdet *(P·T)
    u = dot(tvec, pvec) * inv_det;
    //如果三角形内的点,重心坐标一定在[0,1]
    if (u < 0.0 || u > 1.0f) return false;
    //Q=TxE1
    float3 qvec = cross(tvec, edge1);
    //v=invdet*(Q·D)
    v = dot(ray.direction, qvec) * inv_det;
    if (v < 0.0 || u + v > 1.0f) return false;
    //t=Q·E2  
    t = dot(edge2, qvec) * inv_det;
    return true;
}

关于正反面的判断

\frac{1}{(D\times E_2)\cdot E_1}=\frac{1}{-D\cdot (E_1\times E_2)}  D为射线方向,E1xE2为三角形法线方向 

对于{-D\cdot (E_1\times E_2)
>0 =>cos<0=>夹角>90°=>正面相交

=0 =>cos=0=>夹角=90°=>平行

<0 =>cos>0=>夹角<90°=>反面相交

三角形2:

推导过程:

射线:R(t)=O+tD  (O原点,D方向,t距离)

三角形:T(u,v)=(1-u-v)V_0+uV_1+vV_2(V123 顶点  uvw 重心坐标)

① 确定射线与三角形所在平面相交,并求出交点。

平面:L=<N,-V_0\cdot N> (N为三角形法线)

L\cdot P=0 表示交点在平面内,带入射线

L\cdot (O+tD)=0

t=-\frac{L\cdot O}{L\cdot D}  DL≠0则表示有交点,从而得到交点P

② 计算交点相对于三角形的重心坐标,从而判断是否在三角形内。

交点P带入三角形

P=(1-u-v)V_0+uV_1+vV_2

P-V_0=u(V_1-V_0)+v(V_2-V_0)

设 Ep=P-V0 E1=v1-v0 E2=v2-v0

E_p=uE_1+vE_2

两边乘E1 E2得到二元一次方程

\left\{\begin{matrix} E_p\cdot E_1=uE_1^{2}+vE_1\cdot E_2 \\E_p\cdot E_2=uE_1\cdot E_2+vE_2^2 \end{matrix}\right.

矩阵形式

\begin{bmatrix} E_1^2&E_1\cdot E_2\\E_1\cdot E_2&E_2^2 \end{bmatrix} \begin{bmatrix} u\\v \end{bmatrix}= \begin{bmatrix} E_p\cdot E_1\\E_p\cdot E_2 \end{bmatrix}

求解

\begin{bmatrix} u\\v \end{bmatrix}= \frac{1}{E_1^2E_2^2-(E_1\cdot E_2)^2} \begin{bmatrix} E_2^2&-E_1\cdot E_2\\-E_1\cdot E_2&E_1^2 \end{bmatrix} \begin{bmatrix} E_p\cdot E_1\\E_p\cdot E_2 \end{bmatrix}

bool IntersectTriangle_02(Ray ray, float3 vert0, float3 vert1, float3 vert2,
    inout float t, inout float u, inout float v,inout float toward)
{
    //E1 E2
    float3 edge1 = vert1 - vert0;
    float3 edge2 = vert2 - vert0;
    float3 normal=cross(edge1,edge2);
    //1、检测平面求交点
    float4 plane=float4(normal.xyz,-dot(normal,vert0));
    float ld=dot(plane,float4(ray.direction,0.0f));
    if(ld<EPSILON&&ld>-EPSILON) return false;
    t=-dot(plane,float4(ray.origin,1.0f))/ld;
    
    //正反检测
    toward=sign(-dot(normal,ray.direction));
    
    //Ep
    float3 edgep=ray.origin+ray.direction*t-vert0;
    //2、计算重心坐标
    float e1e2=dot(edge1,edge2);
    float e12=dot(edge1,edge1);
    float e22=dot(edge2,edge2);
    float e1p=dot(edge1,edgep);
    float e2p=dot(edge2,edgep);
    
    float invDet=1.0f/(e12*e22-e1e2*e1e2);
    u=invDet*(e1p*e22-e2p*e1e2);
    if (u < 0.0 || u > 1.0f) return false;
    v=invDet*(-e1p*e1e2+e2p*e12);
    if (v < 0.0 || u + v > 1.0f) return false;
    return true;
}

立方盒:

默认在对象空间计算,需要将光线转至立方盒的对象空间

射线:R(t)=O+tD  (O原点,D方向,t距离)

立方体:x=0;y=0;z=0;x=r_x;y=r_y;z=r_z(由六个面组成)

推导过程:

① 寻找可能正面相交的平面(最多三个)

通过光线方向的三个分量判断各自两个面的相交情况,以x轴为例:

D_x>0:光线与平面x=0相交

D_x<0:光线与平面x=r_x相交

D_x=0 :两个面都不相交

② 计算与平面的焦点,得到最近的相交点(在矩形内部)

R(t)带入各个面,以x=r_x为例

可以确定焦点的x值为r_x,带入R(t)推出t值

t=\frac{r_x-O_x}{D_x}

然后根据t得到交点的另外两个分量

P=O+tD

如果该交点另外两个分量满足下面两个条件,则该点就是最近的相交点,不用再考虑其他平面了。

\left\{\begin{matrix} 0\leq P_y \leq r_y \\ 0\leq P_z \leq r_z \end{matrix}\right.

代码:

#define ISPOINT(name,c1,c2) name.c1>=0&&name.c1<=cubeSize.c1&& name.c2>=0&&name.c2<=cubeSize.c2
//立方体 对象空间
bool IntersectCube(Ray ray,float3 cubeSize,inout float t,inout float3 normal){
    //1.用矩阵将射线转为立方体的对象空间,略 后面的法线也应该转回世界空间 
    float3 rayOri=ray.origin;
    float3 rayDir=ray.direction;
    float3 pos;
    //x轴两个面
    if(rayDir.x<0){ 
        t=(cubeSize.x-rayOri.x)/rayDir.x;
        pos=rayOri+t*rayDir;
        if(ISPOINT(pos,y,z)){
            normal=float3(1,0,0);
            return true;
        }
    }
    else if(rayDir.x>0){
        t=-rayOri.x/rayDir.x;
        pos=rayOri+t*rayDir;
        if(ISPOINT(pos,y,z)){
            normal=float3(-1,0,0);
            return true;
        }
    }
    //y轴两个面
    if(rayDir.y<0){ 
        t=(cubeSize.y-rayOri.y)/rayDir.y;
        pos=rayOri+t*rayDir;
        if(ISPOINT(pos,x,z)){
            normal=float3(0,1,0);
            return true;
        }
    }
    else if(rayDir.y>0){
        t=-rayOri.y/rayDir.y;
        pos=rayOri+t*rayDir;
        if(ISPOINT(pos,x,z)){
            normal=float3(0,-1,0);
            return true;
        }
    }
    //z轴两个面
    if(rayDir.z<0){ 
        t=(cubeSize.z-rayOri.z)/rayDir.z;
        pos=rayOri+t*rayDir;
        if(ISPOINT(pos,x,y)){
            normal=float3(0,0,1);
            return true;
        }
    }
    else if(rayDir.z>0){
        t=-rayOri.z/rayDir.z;
        pos=rayOri+t*rayDir;
        if(ISPOINT(pos,x,y)){
            normal=float3(0,0,-1);
            return true;
        }
    }
    return false;
}

球:

射线:R(t)=O+tD

圆:pos+radius

得到向量d d= pos-O

点乘单位向量得到投影P1 P_1=d\cdot D

两次勾股定律得到p2平方 (P_2)^2=r^2-(d^2-p_1^2)

<0无解 =0 一解 >0 两解

t=P_1\pm P_2

如果有两个解,就选距离最短的那个解,看图可以知道最短的就是进入圆的点,圆的是退出圆。

代码:

bool IntersectSphere(Ray ray, float3 position,float radius,inout float t)
{
    float3 d = ray.origin - position;
    float p1 = -dot(ray.direction, d);
    float p2sqr = p1 * p1 - dot(d, d) +radius*radius;
    if (p2sqr < 0)//不存在解也就是不相交
        return false;
    float p2 = sqrt(p2sqr);
    t = p1 - p2 > 0 ? p1 - p2 : p1 + p2;//优先选近的那个点,除非近的点在后头
    return true;
}

椭球:

这个也是对象空间,这里省略了矩阵变换

推导过程:

射线:R(t)=O+tD  (O原点,D方向,t距离)

椭球:x^2+m^2y^2+n^2z^2=r^2 (m是x半轴和y半轴长度比值,n是x半轴和z半轴长度比值 ,两者等于1就等价于球)

将射线公式带入椭球公式,整理后如下

at^2+bt+c=0\\ \\a=D_x^2+m^2D_y^2+n^2D_z^2\\\\b=2(O_xD_x+m^2O_yD_y+n^2O_zD_z)\\\\ c=O_x^2+m^2O_y^2+n^2O_z^2-r^2

判别式:det=b^2-4ac

交点应该是最近的点:t=\frac{-b- \sqrt{det}}{2a}

计算法线:

转为隐函数,值为0,同时切线和法线的向量积也是0

椭球隐函数:

f(x,y,z)=\frac{x^2}{a^2}+\frac{y^2}{b^2}+\frac{z^2}{c^2}-1

a=r^2;b=(\frac{r}{m})^2;c=(\frac{r}{n})^2

\vec{N}=(\frac{2x}{a^2},\frac{2y}{b^2},\frac{2z}{c^2})

代码:

bool IntersectEllipsoid(Ray ray,float radius,float m,float n,inout float t)
{
    //此处应该有个世界->对象
    float3 rayDir=ray.direction;
    float3 rayOri=ray.origin;
    
    float3 newD=rayDir*float3(1,m,n);
    float3 newO=rayOri*float3(1,m,n);
    float a=dot(newD,newD);
    float b=2*dot(newD,newO);
    float c=dot(newO,newO)-radius*radius;
    float det=b*b-4*a*c;
    if(det<0.0f) return false;
    t=(-b-sqrt(det))/2*a;
    return true;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
光线追踪是一种用于模拟光在三维场景中传播和相互作用的技术。它通过追踪光线的路径来模拟光的传播和反射,从而生成逼真的图像。光线追踪可以用于渲染计算机图形、模拟光学现象等领域。 在光线追踪中,通常会定义一个场景,包括光源、物体和相机。光线从相机出发,经过场景中的物体,根据物体的材质属进行反射、折射等操作,最终到达光源或者被吸收。通过追踪每条光线的路径,可以计算出每个像素点的颜色值,从而生成图像。 Matlab是一种强大的科学计算软件,也可以用于光线追踪的实现。在Matlab中,可以使用矩阵运算和向量化编程来高效地进行光线追踪算法的实现。同时,Matlab还提供了丰富的图形绘制和可视化工具,可以方便地展示光线追踪生成的图像结果。 如果你想在Matlab中实现光线追踪,可以按照以下步骤进行: 1. 定义场景:包括光源、物体和相机等参数。 2. 发射光线:从相机出发,确定光线的起点和方向。 3. 碰撞检测:判断光线是否与物体相交,计算交点位置和法向量等信息。 4. 光线反射和折射:根据物体的材质属,计算光线的反射和折射方向。 5. 路径追踪:根据反射和折射方向,继续追踪光线的路径,直到光线到达光源或被吸收。 6. 计算颜色:根据光线的路径和物体的材质属,计算每个像素点的颜色值。 7. 生成图像:将计算得到的颜色值转换为图像,并进行显示或保存。 希望以上介绍对你有帮助!如果你有任何相关问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值