Ray-Triangle intersection

思路概述

射线和三角形求交的思路很简单,求出射线和三角形所在的平面的交点,然后算这个交点的在三角形的重心坐标,重心坐标在[0, 1]之间,说明射线和三角形相交,否则不相交。

假设我么的ray的方程是

Ray = O + tD

这个计算总体来说是两步,第一步求射线和平面的交点,只需要求出t。第二步是求三角中心坐标u和v,我们想找出一个更高效的方法,叫unit triangle intersection method。

简单来说,就是把三角形的三个点变换到(0, 0, 0),(1, 0, 0),(0, 1, 0)这三个点构成的空间里,然后射线和xoy平面相交即可,如下图所示:

注意,这个图是右手坐标。

这里用一个Affine transform来做变换,Affine transform的意思是,直线变换后仍然是直线,如果变换后是曲线,就不是Affine transform了,所以我们日常用的平移,缩放,旋转,都是Affine transform。

公式推导

我们设定这个Affine transform的变换过程是T(P),P是世界坐标中一点。

三角形的三个点是ABC,那么有:

T(A) = \begin{pmatrix} 1\\ 0\\ 0 \end{pmatrix},   T(B) = \begin{pmatrix} 0\\ 1\\ 0 \end{pmatrix},    T(C) = \begin{pmatrix} 0\\ 0\\ 0 \end{pmatrix},    T(N) = \begin{pmatrix} 0\\ 0\\ 1 \end{pmatrix}

那么T的逆变换就是:

所以我们可以构造T变换的逆变换为:

T^{-1}(P) = \begin{bmatrix} A_x - C_x & B_x - C_x & N_x - C_x\\ A_y - C_y & B_y - C_y & N_y - C_y\\ A_z - C_z & B_z - C_z & N_z - C_z \end{bmatrix} P + \begin{pmatrix} C_x\\ C_y\\ C_z \end{pmatrix}

最好我们可以用矩阵的形式去表示这个逆变换:

M^{-1}=\begin{bmatrix} A_x - C_x & B_x - C_x & N_x - C_x & C_x\\ A_y - C_y & B_y - C_y & N_y - C_y & C_y\\ A_z - C_z & B_z - C_z & N_z - C_z & C_z\\ 0 & 0 & 0 & 1 \end{bmatrix}

那么变换到Unit triangle空间中的Ray就是

R'=MR=M(O + tD) = O' + tD'

由于射线和xoy平面相交,也就是交点的z坐标为0.

P_{intersect}=\begin{pmatrix} O'_x + tD'_x\\ O'_y + tD'_y\\ O'_z + tD'_z = 0 \end{pmatrix}

所以t可以很方便的计算:

t=-\frac{O'_z}{D'_z}

下面看三角形的重心坐标u v如何计算。

交点在xoy平面上的重心坐标可以表示为:

O'+tD'=(1 - u - v)C' + uA' + vB'

由于C'就是原点0,A' = (1, 0, 0),B' = (0, 1, 0),那么展开向量:

\begin{pmatrix} O'_x + tD'_x\\ O'_y + tD'_y\\ 0 \end{pmatrix} =u\begin{pmatrix} 1\\ 0\\ 0 \end{pmatrix} + v\begin{pmatrix} 0\\ 1\\ 0 \end{pmatrix}

最后得到的uv是:

u=O'_x + tD'_x, \quad v=O'_y + tD'_y

整个过程计算完毕,是不是很简单呢!

代码实现

第一步:构建矩阵M

Matrix4x4 matrix = new Matrix4x4();
Vector4 col0 = v0 - v2;
col0.w = 0;
matrix.SetColumn(0, col0);
Vector4 col1 = v1 - v2;
col1.w = 0;
matrix.SetColumn(1, col1);
Vector4 col2 = Vector3.Cross(v0 - v2, v1 - v2);
col2.w = 0;
matrix.SetColumn(2, col2);
Vector4 col3 = v2;
col3.w = 1;
matrix.SetColumn(3, col3);
matrix = Matrix4x4.Inverse(matrix);

第二步,把矩阵写入buffer中,这里我不贴代码了,简单来说就是compute shader或者cuda里在求交时要访问的bvh叶子节点。

第三步,就是求交的代码,假设我把矩阵M的每一行传递到buffer里。

我们要求的是O'和D',那么根据矩阵和向量的乘法,求t的代码如下:

const float4 m0 = Positions[triAddr];     //matrix row 0 
const float4 m1 = Positions[triAddr + 1]; //matrix row 1 
const float4 m2 = Positions[triAddr + 2]; //matrix row 2

//Oz is a point, must plus w
float Oz = m2.w + origx * m2.x + origy * m2.y + origz * m2.z;
//Dz is a vector
float invDz = 1.0f / (dirx * m2.x + diry * m2.y + dirz * m2.z);
float t = -Oz * invDz;

下面是求u和v的代码。

//if t is in bounding and less than the ray.tMax
if (t > tmin && t < hitT)
{
    // Compute and check barycentric u.
    float Ox = m0.w + origx * m0.x + origy * m0.y + origz * m0.z;
    float Dx = dirx * m0.x + diry * m0.y + dirz * m0.z;
    float u = Ox + t * Dx;

    if (u >= 0.0f)
    {
	    // Compute and check barycentric v.
	    float Oy = m1.w + origx * m1.x + origy * m1.y + origz * m1.z;
	    float Dy = dirx * m1.x + diry * m1.y + dirz * m1.z;
	    float v = Oy + t * Dy;

	    if (v >= 0.0f && u + v <= 1.0f)
	    {
		    // Record intersection.
		    // Closest intersection not required => terminate.
		    hitT = t;
		    hitIndex = triAddr;
	    }
    }
}

参考

http://www.sven-woop.de/papers/2004-GH-SaarCOR.pdf

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 根据三角形不等式,我们可以编写一段C语言代码来判断三条线段的长度能否组成三角形,如果不能,则输出No,如果能,请判断此三角形是否为等边三角形,若是则输出E-Triangle,否则输出G-Triangle。以下是编写的C语言程序:#include<stdio.h> int main() { int a,b,c; scanf("%d %d %d",&a,&b,&c); if(a+b>c && a+c>b && b+c>a) { if(a==b && a==c) printf("E-Triangle"); else printf("G-Triangle"); } else printf("No"); return 0; } ### 回答2: 可以通过以下步骤来判断三条线段能否组成三角形,并判断是否为等边三角形: 1. 首先,输入三个整数表示三条线段的长度,分别为a、b和c。 2. 判断三个数是否大于0,若有任一条线段长度小于等于0,则输出"No"。 3. 判断是否满足三角形的三边关系,即任意两边之和大于第三边。若不满足该条件,则输出"No"。 4. 判断是否为等边三角形,若三个数都相等,则输出"E-Triangle";否则,输出"G-Triangle"。 以下是对应的C语言代码实现: ```c #include<stdio.h> int main() { int a, b, c; // 输入三个整数 printf("请输入三个整数,分别表示三条线段的长度:\n"); scanf("%d%d%d", &a, &b, &c); // 判断三角形是否合法 if (a <= 0 || b <= 0 || c <= 0 || (a+b <= c) || (a+c <= b) || (b+c <= a)) { printf("No\n"); } else { // 判断是否为等边三角形 if (a == b && b == c) { printf("E-Triangle\n"); } else { printf("G-Triangle\n"); } } return 0; } ``` 该程序首先通过`scanf`函数输入三个整数,判断是否满足三角形的三边关系,并判断是否为等边三角形,并输出相应结果。 ### 回答3: 可以根据三角形的性质来判断三条线段能否组成一个三角形。三角形的任意两边之和大于第三边,任意两边之差小于第三边。所以我们可以编写一个判断三角形的函数。再判断是否为等边三角形。 首先,定义一个函数isTriangle(int a, int b, int c),用于判断三条边a、b、c能否组成一个三角形: ```c int isTriangle(int a, int b, int c) { if (a + b > c && a + c > b && b + c > a) { return 1; } else { return 0; } } ``` 再定义一个函数isEquilateral(int a, int b, int c),用于判断三角形是否为等边三角形: ```c int isEquilateral(int a, int b, int c) { if (a == b && b == c) { return 1; } else { return 0; } } ``` 在主函数中,读入三个整数表示三条线段的长度,调用isTriangle函数判断是否为三角形,如果不是则输出"No",否则调用isEquilateral函数判断是否为等边三角形,如果是则输出"E-Triangle",否则输出"G-Triangle"。 ```c #include <stdio.h> int isTriangle(int a, int b, int c) { if (a + b > c && a + c > b && b + c > a) { return 1; } else { return 0; } } int isEquilateral(int a, int b, int c) { if (a == b && b == c) { return 1; } else { return 0; } } int main() { int a, b, c; printf("请输入三条线段的长度:"); scanf("%d %d %d", &a, &b, &c); if (!isTriangle(a, b, c)) { printf("No\n"); } else if (isEquilateral(a, b, c)) { printf("E-Triangle\n"); } else { printf("G-Triangle\n"); } return 0; } ``` 以上就是判断三条线段是否能组成三角形以及是否为等边三角形的C语言程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值