[图形学] 射线和线段之间的最小距离

news2024/10/7 2:18:08

1 说在前面

本文的主要内容来自于Unity引擎中Spline功能的一个函数,一开始我难以理解这几个向量运算的作用和几何意义,经过一番思考后总结如下:
该段代码实际上更像是两个直线之间寻找最短距离,然后判断该距离对应的点在其中一条直线(代码中称为线段)上的位置(线段前、线段中、线段后);由于代码中以射线-线段的关系来描述这个问题,本文中也统一以射线和线段称呼两者的关系,在以后再次提到时就不做补充说明了;

函数的具体内容为:

/// <summary>
/// Returns the parameterization of a ray line projection. The parameter will be negative if the nearest point
/// between the ray/line is negative to 'lineOrigin', and greater than 1 if nearest intersection is past the end
/// off the line segment (lineOrigin + lineDir).
/// </summary>
/// <param name="ro">The ray origin point.</param>
/// <param name="rd">The ray direction (normalized vector).</param>
/// <param name="lineOrigin">Line segment first point.</param>
/// <param name="lineDir">Line segment direction (with magnitude).</param>
/// <returns>The parameter of a ray line projection.</returns>
public static float RayLineParameter(float3 ro, float3 rd, float3 lineOrigin, float3 lineDir)
{
    var v0 = ro - lineOrigin;
    var v1 = math.cross(rd, math.cross(rd, lineDir));
    // the parameter of a ray to line projection will be negative if the intersection is negative to line
    // direction from 'a', and greater than 1 if intersection is past the line segment end 'b'
    return math.dot(v0, v1) / math.dot(lineDir, v1);
}

该函数实现了:输入一个射线的起点与方向:ro、rd,以及一个线段的起点和朝向(包括了线段的长度)。返回一个float数值,表示射线到线段的最小距离对应的点在线段上的位置,(0-1)之间表示在线段上,(-∞,0)表示在线段起点之前,(0, +∞)表示在线段重点之,该数值约接近0表示距离线段起点越近,越接近1表示距离线段终点越近。一些特例如下图所示:
在这里插入图片描述
需要注意,射线和线段都是用float3表示的,它们存在于三维空间中,以上例子只不过是刚好同处于一个二维平面下的特例。

2 运算

该计算涉及两个向量:射线向量和线段向量,分别用a和l表示,进一步的,将向量的首末端点表示为 a 0 , a 1 a_0, a_1 a0,a1 l 0 , l 1 l_0, l_1 l0,l1,如下图所示:
在这里插入图片描述

这段代码其实很短,只计算了两个中间变量 v 0 , v 1 v_0, v_1 v0,v1,随后进行了返回值的计算;
v 0 v_0 v0:意义非常明显,表示为向量 V e c t o r ( l 0 , r 0 ) Vector(l_0, r0) Vector(l0,r0),如下图所示:
在这里插入图片描述
v 1 v_1 v1:则相对复杂一点,其经过了两次向量叉乘,首先:
r d × l i n e D i r r_d\times lineDir rd×lineDir:获得一个与射线和线段都垂直的向量,即与 V e c t o r ( r 0 , r 1 ) Vector(r_0, r_1) Vector(r0,r1) V e c t o r ( l 0 , l 1 ) Vector(l_0, l_1) Vector(l0,l1)都垂直的向量;
r d × ( r d × l i n e D i r ) r_d\times(r_d\times lineDir) rd×rd×lineDir:在此基础上又将该向量和 r d r_d rd叉乘得到中间向量 v 1 v_1 v1;
上述整个过程如下图所示:
在这里插入图片描述
仔细观察,可以发现 v 1 v_1 v1 r d r_d rd l i n e D i r lineDir lineDir共面的,因为 r d × l i n e D i r r_d\times lineDir rd×lineDir垂直于 r d r_d rd l i n e D i r lineDir lineDir所在平面,再与 r d r_d rd叉乘后的结果又回到了 r d r_d rd l i n e D i r lineDir lineDir所在平面。
③result:返回值通过中间计算量得到,有:
r e s u l t = v 0 ⋅ v 1 l i n e D i r ⋅ v 1 result = \frac{v_0\cdot v_1}{lineDir\cdot v_1} result=lineDirv1v0v1几何关系如下图所示在这里插入图片描述

3 原理

3.1 坐标系变换

result为何具备文章开头时所说的几何意义呢,仔细观察可以发现,该计算结果可以换个视角,将 v 1 v_1 v1 r d r_d rd l i n e D i r lineDir lineDir所在平面视作yoz平面,其中 r d r_d rd指向z轴方向, v 1 v_1 v1指向y轴负方向,result的计算可以转化为下图中右侧的部分:
在这里插入图片描述

注意!注意!注意!向量 v 0 v_0 v0是不在yoz平面上的!,从这个新的坐标系理解
result:代表了 v 0 v_0 v0 l i n e D i r lineDir lineDir在新y轴上的投影结果a、b的比值,只不过本文例子中a刚好为0;此时result=0,表示射线到线段的最短点刚好为线段的起点。

3.2 为什么是y轴上投影

我们来看 v 0 v_0 v0的变化对结果的影响,先简单地将射线和线段起点重合,此时很容易看出,射线到线段上最短距离对应的点就是原点,距离为0,如下图所示:
在这里插入图片描述
将目标线段沿着x’轴移动,发现最短的点仍然为线段的原点,射线上对应的仍然是原点,只不过最短距离发生了变化,也就是说在x’方向上的距离分量是无法通过调整射线上和线段上点的位置缩短的,其增减的是不可更改的距离,如下图所示:
在这里插入图片描述

同样的,将目标线段沿着z’轴移动,发现最短的点仍然为线段的原点,最短距离也没有发生变化,只不过射线上对应的点改变了,也就是说在z’方向上的距离分量是可以通过调整射线上点的位置抵消的,如下图所示:
在这里插入图片描述
最后,将目标线段沿着y’轴移动,发现最短的点在线段上的位置改变了,分别在线段终点后,线段上和线段起点前,射线上对应的点也改变了,但是最短距离没有改变,这就是为什么需要y轴上的投影了
在这里插入图片描述

3.3 result的几何意义

为什么result能够描述最近点在目标线段什么位置呢?先看下图:
在这里插入图片描述
可以看出,只有当线段落在lineLeft和lineRight之间时(不考虑x、z轴的变化),射线和线段之间的最短距离点才会落在线段上,而lineLeft和lineRight之间的长度正是 l i n e D i r ⋅ v 1 lineDir\cdot v_1 lineDirv1,即result的 分母;
曲线再往左或者再往右对应了最短距离点再线段的哪一侧,result的几何意义就是这样。

4 总结

①射线的方向向量和线段的方向向量构成了一个新的参考坐标系x’y’z’,该坐标系中射线方向向量是y轴,两个方向向量在yoz平面上;
②result的分母,也就是b只和两个因素有关:两个方向向量的夹角、以及线段长度;这很好理解:
1、当两个向量夹角越小,线段上点到射线上一点的跨度就越小,反之跨度越大。因此夹角越小时最短距离点在线段上的可能越小,反之亦然;
2、线段越长,最短距离点在线段上的可能性越大;
③result的分子,也就是a只与射线和线段的起点构成的距离向量相关,且该距离向量在新坐标系下x、z轴上的分量是不影响结果的;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32利用USB的HID与QT上位机通信

之前使用kingst的逻辑分析仪&#xff0c;打开上位机软件&#xff0c;插上带usb的硬件就可以通信&#xff0c;也不需要打开串口什么的&#xff0c;感觉很方便&#xff0c;于是借用一个周末研究下这个技术。本文主要是用于记录自己学习的过程&#xff0c;顺便分享下学习感悟。 首…

大数据周会-本周学习内容总结012

开会时间&#xff1a;2023.05.07 16:00 线下会议 目录 01【es数据同步至mysql】 1.1【在es中插入数据后能够同步到mysql中】 1.2【修改与删除es中的数据】 02【nifi】 2.1【Nifi的单机及分布式集群部署】 2.2【nifi集群&#xff0c;getFile简单使用nifi】 2.3【nifi使用…

如何利用Requestly提升前端开发与测试的效率,让你事半功倍?

痛点 前端测试 在进行前端页面开发或者测试的时候&#xff0c;我们会遇到这一类场景&#xff1a; 在开发阶段&#xff0c;前端想通过调用真实的接口返回响应在开发或者生产阶段需要验证前端页面的一些 异常场景 或者 临界值 时在测试阶段&#xff0c;想直接通过修改接口响应来…

Nuvoton NK-980IOT开发板 u-boot 编译

前言 最近搭建了 Nuvoton NK-980IOT开发板 的开发编译环境&#xff0c;记录一下 u-boot 的 编译流程 Nuvoton NK-980IOT开发板 资源还是比较的丰富的&#xff0c;可以用于 嵌入式Linux 或者 RT-Thread 的学习开发 开发板上电比较的容易&#xff0c;两根 USB 线即可&#xff0…

进程与线程(二)

进程同步、进程互斥 同步亦称直接制约关系&#xff0c;是指为完成某种任务而建立的两个或多个进程&#xff0c;这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。进程间的直接制约关系就是源于他们之间的相互合作。 操作系统要提供“进程同步机制”来解决异…

Oracle的学习心得和知识总结(二十四)|Oracle数据库DBMS程序包解密方法及SQL Developer和Unwrapper的安装与使用

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…

android 隐藏底部虚拟按键

方法一 滑动屏幕 可重新显示出来 protected void hideBottomUIMenu() { //隐藏虚拟按键&#xff0c;并且全屏 if (Build.VERSION.SDK_INT <11 && Build.VERSION.SDK_INT < 19) { // lower api View v this.getWindow().getDecorView(); v.setSyst…

大众软件组织人事地震:传董事会被裁,5000人的CARIAD何去何从?

作者 | 德新 编辑 | 王博 外媒Business Insider近日爆出一则重磅消息&#xff1a;大众汽车集团CEO Oliver Blume&#xff08;奥博穆&#xff09;有意裁掉旗下软件组织CARIAD的整个董事会。其影响的高层包括&#xff0c;CARIAD CEO Dirk Hilgenberg、CTO Lynn Longo&#xff0c;…

influxdb时序型数据库基础

文章目录 什么是InfluxDB时序数据特点常见应该场景时序数据库解决什么问题InfluxDB的优势InfluxDB常用命令 什么是InfluxDB InfluxDB是一个开源的、高性能的时序型数据库&#xff0c;在时序型数据库DB-Engines Ranking上排名第一。 在介绍InfluxDB之前&#xff0c;先来介绍下…

机器学习随记(5)—决策树

手搓决策树&#xff1a;用决策树将其应用于分类蘑菇是可食用还是有毒的任务 温馨提示&#xff1a;下面为不完全代码&#xff0c;只是每个步骤代码的实现&#xff0c;需要完整跑通代码的同学不建议花时间看&#xff1b;适合了解决策树各个流程及代码实现的同学复习使用。 1 数据…

MySQL锁机制

目录 表级锁&行级锁 排他锁&共享锁 InnoDB行级锁 行级锁&#xff08;record lock&#xff09;&#xff1a; 间隙锁&#xff08;gap lock&#xff09;&#xff1a; 意向锁 InnoDB表级锁 MVCC&#xff08;多版本并发控制&#xff09; 已提交读的MVCC&#xff1a…

Linux下的shell

NC反向shell 1、查看shell类型 echo $SHELLchsh -s 需要修改shell的类型cat /etc/shells 查看存在哪些shell 然后反弹对应的shell&#xff08;正向连接&#xff09; //被控制端 nc -lvvp 8989 -e /bin/bash //控制端 nc 192.168.222.146(被控端ip) 8989 2、没有-e参数反…

css链接悬停时滑动的下划线效果

要创建链接悬停时滑动的下划线效果&#xff0c;可以向锚点标记添加伪元素&#xff0c;并使用 CSS 过渡动画来显示它。 先看效果&#xff1a; 在提供的代码中&#xff0c;a::after 选择器创建了一个伪元素&#xff0c;该伪元素位于 a 标记后面。该伪元素具有绿色背景颜色和 1…

KVM 架构和部署

建议使用centos和ubuntu 系统做实验&#xff0c;rocky 系列有些不太支持 宿主机环境准备 KVM需要宿主机CPU必须支持虚拟化功能&#xff0c;因此如果是在vmware workstation上使用虚拟机做宿主机&#xff0c;那么必须要在虚拟机配置界面的处理器选项中开启虚拟机化功能。 验证…

【AI选股】如何通过python调用通达信-小达实现AI选股(量化又多了一个选股工具)

文章目录 前言一、通达信-小达是什么&#xff1f;二、使用步骤1. 引入browser_cookie3库2. 通达信-小达 AI选股源代码 总结 前言 ChatGPT火遍网络&#xff0c;那么有没有可以不用写公式就可以实现AI选股的方法&#xff1f;答案是有&#xff0c;今天我们就来试试通达信的小达&a…

软件测试面试常见问题【含答案】

一、面试技巧题(主观题) 序号面试题1怎么能在技术没有那么合格的前提下给面试官留个好印象&#xff1f;2面试时&#xff0c;如何巧妙地避开不会的问题&#xff1f;面试遇到自己不会的问题如何机智的接话&#xff0c;化被动为主动&#xff1f;3对于了解程度的技能&#xff0c;被…

鸿蒙学习总结

控件 button 源码所在路径&#xff0c;小编也只是猜测&#xff0c;还没搞懂鸿蒙上层app到底层的玩法&#xff0c;网上也没相关资料&#xff0c;找源码真是费劲(不是简单的下载个源码的压缩包&#xff0c;而是找到里面的控件比如Button&#xff0c;或者UIAbility实现的源码&…

探索qrc,rcc和CMAKE_AUTORCC

前导知识&#xff1a;解决qt中cmake单独存放 .ui, .cpp, .h文件 前言 我们的Qt程序可以加载一些资源&#xff0c;比如程序窗口的图标。 像下面这样的原始图标&#xff0c;很丑。 可以给它加上图标&#xff0c;一个小海豚。 一、直接加载资源 这是最简单直接的方式。 …

IntersectionObserver API实现虚拟列表滚动

前言 在本篇文章你将会学到&#xff1a; IntersectionObserver API 的用法&#xff0c;以及如何兼容。如何在React Hook中实现无限滚动。如何正确渲染多达10000个元素的列表。 无限下拉加载技术使用户在大量成块的内容面前一直滚动查看。这种方法是在你向下滚动的时候不断加…

keil MDK5软件包介绍、下载、安装与分享

前言 本文介绍了Keil MDK5软件包的分类、作用、下载、安装与更新。软件包下载可通过Keil自带的Pack Installer、进入Keil Pack下载网站手动下载、去芯片厂家官网下载三种方式。同时分享了一个小技巧&#xff0c;可以直接分享已安装好的软件包给别人。 一. Keil MDK软件包介绍 K…