射线与球体/三角面片求交、重心坐标、插值

射线

已知射线方程为 P ( t ) = P ˉ + t ∗ D ˉ P(t)=\bar{P}+t*\bar{D} P(t)=Pˉ+tDˉ,其中 P ˉ \bar{P} Pˉ为射线的起点 ( x 0 , y 0 , z 0 ) (x_0,y_0,z_0) (x0,y0,z0) D ˉ \bar{D} Dˉ为射线的方向向量,其模长为 1 1 1,只要知道距离 t t t,我们就可以计算出对应点的坐标。

球体求交

在这里插入图片描述

不妨设球体的方程为 ( X ˉ − C ˉ ) 2 = R 2 (\bar{X}-\bar{C})^2=R^2 (XˉCˉ)2=R2,其中 X ˉ \bar{X} Xˉ为球体上任意一点的坐标, C ˉ \bar{C} Cˉ为球心, R R R为半径。射线与球体求交,首先需要将射线方程作为 X ˉ \bar{X} Xˉ带入球体方程,然后把 t t t作为未知变量,化简即可得到下式: D 2 t 2 + 2 ( P ˉ ∗ D ˉ − C ˉ ∗ D ˉ ) t − 2 C ˉ ∗ P ˉ + P 2 + C 2 − R 2 = 0 D^2t^2+2(\bar{P}*\bar{D}-\bar{C}*\bar{D})t-2\bar{C}*\bar{P}+P^2+C^2-R^2=0 D2t2+2(PˉDˉCˉDˉ)t2CˉPˉ+P2+C2R2=0
显然我们可以把上式当作一个关于 t t t的一元二次方程: a t 2 + b t + c = 0 at^2+bt+c=0 at2+bt+c=0,判断是否存在交点即判断该一元二次方程的判别式: Δ = b 2 − 4 ∗ a ∗ c Δ=b^2-4*a*c Δ=b24ac是否大于等于 0 0 0,如果是的话说明方程有解,通过求根公式得到两个解: t 1 = − b 2 − 4 a c / ( 2 ∣ a ∣ ) − b / ( 2 a ) t_1=-\sqrt{b^2-4ac}/(2|a|)-b/(2a) t1=b24ac /(2a)b/(2a) t 2 = b 2 − 4 a c / ( 2 ∣ a ∣ ) − b / ( 2 a ) t_2=\sqrt{b^2-4ac}/(2|a|)-b/(2a) t2=b24ac /(2a)b/(2a)排除掉无效解(<0的解)后,取最小的那个解即可。

三角求交

在这里插入图片描述
这个过程可以分成两步, ( 1 ) (1) (1)计算射线和平面 A B C ABC ABC的交点 Q Q Q ( 2 ) (2) (2)判断 Q Q Q是否在三角形 A B C ABC ABC内部。

计算交点Q

我们知道一个平面方程可以写成 a x + b y + c z = d ax+by+cz=d ax+by+cz=d的形式,且这个平面的法向量为 ( a , b , c ) (a,b,c) (a,b,c),证明如下:在平面上任取两点 A 、 B A、B AB,显然有 a x 1 + b y 1 + c z 1 = d , a x 2 + b y 2 + c z 2 = d ax_1+by_1+cz_1=d,ax_2+by_2+cz_2=d ax1+by1+cz1=d,ax2+by2+cz2=d,两式相减可得: a ( x 2 − x 1 ) + b ( y 2 − y 1 ) + c ( z 2 − z 1 ) = 0 a(x_2-x_1)+b(y_2-y_1)+c(z_2-z_1)=0 a(x2x1)+b(y2y1)+c(z2z1)=0,假设 n = ( a , b , c ) n=(a,b,c) n=(a,b,c),上市其实就等于 n ˉ ∗ A B ˉ = 0 \bar{n}*\bar{AB}=0 nˉABˉ=0,所以 n ˉ \bar{n} nˉ就是平面的法向量。那么这个平面方程可以写成: n ˉ ∗ x ˉ = d \bar{n}*\bar{x}=d nˉxˉ=d,将射线方程带入化简可得:
在这里插入图片描述
所以我们需要先判断 n ˉ ∗ d ˉ \bar{n}*\bar{d} nˉdˉ是否为 0 0 0,如果为 0 0 0说明射线和平面平行,否则可以根据上式计算出 t t t从而计算出 Q Q Q的坐标。

关于平面的一些问题

在这里插入图片描述
在这里插入图片描述
上面的推导来源于平面方程 a x + b y + c z = d ax+by+cz=d ax+by+cz=d,然而现在我们仅仅知道三角形三个顶点 A 、 B 、 C A、B、C ABC的坐标,如何计算 n ˉ \bar{n} nˉ d d d就成为一个问题。计算法向量比较简单,利用叉积即可完成: n ˉ = A B ˉ × A C ˉ \bar{n}=\bar{AB}×\bar{AC} nˉ=ABˉ×ACˉ(记得归一化),那么由之前的推导可知 d = n ˉ ∗ x ˉ d=\bar{n}*\bar{x} d=nˉxˉ x ˉ \bar{x} xˉ为平面内任意点的坐标,我们可以选取顶点 A A A d = n ˉ ∗ A d=\bar{n}*A d=nˉA(上面这个英文 p d f pdf pdf写得这么多,只是在探讨叉积的方向问题)。

判断Q是否在三角形内部

在这里插入图片描述
在这里插入图片描述
简单来说,就是通过叉乘的方向来判断。可以看上面的图示,感觉比较清晰。这个操作我们称为 3 D   i n s i d e − o u t s i d e   t e s t 3D\ inside-outside\ test 3D insideoutside test
在这里插入图片描述
3 D 3D 3D内外检测需要的计算量比较大,我们可以考虑将三角形投影到 2 D 2D 2D平面上,然后再做 2 D   i n s i d e − o u t s i d e   t e s t 2D\ inside-outside\ test 2D insideoutside test。如何选择投影平面呢?首先计算出法向量 N ˉ \bar{N} Nˉ,取它的三个维度 x 、 y 、 z x、y、z xyz中绝对值最大的那个维度消去即可。比如 N ˉ = ( 0 , 0 , − 1 ) \bar{N}=(0,0,-1) Nˉ=(0,0,1),就消去维度 z z z,将其投影到 x O y xOy xOy平面上。

计算重心坐标

在这里插入图片描述
我们称 α 、 β 、 γ α、β、γ αβγ Q Q Q的关于 A 、 B 、 C A、B、C ABC的重心坐标。 α 、 β 、 γ α、β、γ αβγ可以通过面积比来计算出来,如下图:
在这里插入图片描述
在这里插入图片描述
重心坐标可以用于插值,比如材质性能、纹理坐标、法向量。其中法向量插值又称为 P h o n g Phong Phong插值,可以为三角形网格提供更加平滑的着色外观。下面介绍一下法向量插值:
在这里插入图片描述
在这里插入图片描述
根据三角形面积的比值计算出 α 、 β 、 γ α、β、γ αβγ,然后根据三个顶点的法向量进行插值, N Q N_Q NQ可以用于后续的着色、反射和折射计算。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于C++的简单代码示例: ``` #include <iostream> #include <vector> #include <cmath> using namespace std; struct Vector3 { float x, y, z; Vector3 operator+(const Vector3& v) const { return { x + v.x, y + v.y, z + v.z }; } Vector3 operator-(const Vector3& v) const { return { x - v.x, y - v.y, z - v.z }; } Vector3 operator*(float f) const { return { x * f, y * f, z * f }; } Vector3 cross(const Vector3& v) const { return { y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x }; } float dot(const Vector3& v) const { return x * v.x + y * v.y + z * v.z; } float length() const { return sqrtf(x * x + y * y + z * z); } Vector3 normalize() const { float len = length(); return { x / len, y / len, z / len }; } }; struct Ray { Vector3 origin; Vector3 direction; }; struct Triangle { Vector3 v0, v1, v2; Vector3 normal() const { return (v1 - v0).cross(v2 - v0).normalize(); } }; // 判断射线三角形是否相 bool intersectRayTriangle(const Ray& ray, const Triangle& triangle, float& t) { Vector3 e1 = triangle.v1 - triangle.v0; Vector3 e2 = triangle.v2 - triangle.v0; Vector3 h = ray.direction.cross(e2); float a = e1.dot(h); if (a > -1e-6f && a < 1e-6f) { return false; } float f = 1.0f / a; Vector3 s = ray.origin - triangle.v0; float u = f * s.dot(h); if (u < 0.0f || u > 1.0f) { return false; } Vector3 q = s.cross(e1); float v = f * ray.direction.dot(q); if (v < 0.0f || u + v > 1.0f) { return false; } t = f * e2.dot(q); return t > 1e-6f; } int main() { // 构建射线 Ray ray = { { 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f }.normalize() }; // 构建三角形 Triangle triangle = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; // float t; if (intersectRayTriangle(ray, triangle, t)) { cout << "Intersection at t = " << t << endl; } else { cout << "No intersection" << endl; } return 0; } ``` 在这个示例中,我们定义了三个结构体:`Vector3`表示三维向量,`Ray`表示射线,`Triangle`表示三角形。接着,我们实现了一个名为`intersectRayTriangle`的函数来判断射线三角形是否相,如果相,函数将返回`true`并返回相点的参数`t`,否则返回`false`。 函数的实现基于Möller-Trumbore算法,该算法是一种高效的射线三角算法。 最后,在`main`函数中,我们构建了一个射线和一个三角形,并使用`intersectRayTriangle`函数来。如果有点,我们输出参数`t`,否则输出"No intersection"。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值