射线与三角形所在平面相交,并不表示它真的碰到了三角形。交点可能在边外,或者位于射线起点后方,因此需要同时检查方向参数和三角形内部坐标。
本例射线为起点加t乘方向,要求t不小于零。先用三角面法线求射线与平面的交点,再把交点写成顶点A加u乘边AB加v乘边AC。只有u、v均非负且u加v不大于一时才在三角形内,边界也算命中。
三角形顶点依次为[3, -1, 3]、[7, -1, 4]、[3, 1, 5]。七条射线独立检查。
射线起点、方向 | 结果 {'origin': ['5', '0', '9/2'], 'direction': [4, 0, 1]} | {'status': '共面'} {'origin': ['4', '-1/2', '19/4'], 'direction': [4, 0, 1]} | {'status': '平行不共面'} {'origin': ['5', '0', '29/2'], 'direction': [0, 0, 5]} | {'status': '起点后方', 't': '-2', 'u': '1/2', 'v': '1/2', 'point': ['5', '0', '9/2']} {'origin': ['0', '2', '-25/4'], 'direction': [1, -1, 5]} | {'status': '平面交点在面外', 't': '2', 'u': '-1/4', 'v': '1/2', 'point': ['2', '0', '15/4']} {'origin': ['7', '-8', '-16'], 'direction': [-1, 2, 5]} | {'status': '命中', 't': '4', 'u': '0', 'v': '1/2', 'point': ['3', '0', '4']} {'origin': ['11', '3/2', '0'], 'direction': [-2, -2, 5]} | {'status': '平面交点在面外', 't': '1', 'u': '3/2', 'v': '1/4', 'point': ['9', '-1/2', '5']} {'origin': ['7', '0', '-16'], 'direction': [-1, 0, 5]} | {'status': '命中', 't': '4', 'u': '0', 'v': '1/2', 'point': ['3', '0', '4']}表中的编号仅用于区分当前案例里的对象,不能与数值大小或另一份记录中的身份混淆。先核对输入、单位和边界约定,再跟随过程检查输出。空列表、不存在和数值零各有不同含义,分数则保留精确形式便于复算。
方向与法线点积为零时,射线平行于平面,要区分不在平面和恰好共面。本例对共面情形单独标记,不继续假装存在唯一交点。方向向量不要求单位长度,因此t不是自动等于几何距离。
先检查平面残差为零,再检查重心系数与射线参数。若坐标采用浮点,边界附近还需明确容差;本例使用精确分数,因此等号可以直接比较。不要用渲染表面看起来朝向哪里来代替数学上的命中条件。
示例使用双面三角形,不根据法线朝向进行背面剔除。顶点不共线且方向不为零;真实场景还可能有多个三角形,需要再取最近的合法命中,而不能只返回第一个被遍历到的面。
这是一份可重复演算的游戏知识记录,结论只对写明的输入与规则成立。练习时可以先改变一个条件,再对照中间记录寻找变化从哪一步开始。若结果不同,应先确认操作顺序和边界定义,而不是只改最后一个数字;原始输入与完整过程应一起保留。