three.js - 围绕保持最小接近度的球体旋转矩形精灵
three.js - rotate a rectangular sprite around a sphere maintaining minimum proximity
给定一个 three.js 场景和 静态相机 ,一个 sphere 0,0,0
和任意尺寸的 矩形精灵 (例如文本标签),我正在寻找允许旋转精灵的 'threejs method' (或公式)围绕球体而不剪裁,以尽可能小的半径。
到目前为止,我的方法是计算球体上位置的极坐标,然后在精灵接近球体原点时将其偏移活动维度的一个因子。我已将其稍微调整为:
const xPolar = Math.sin(phi) * Math.sin(theta);
const yPolar = Math.cos(phi);
const zPolar = Math.sin(phi) * Math.cos(theta);
const x = xPolar + sprite.radius * xPolar;
const y = yPolar + sprite.radius * yPolar;
const z = zPolar + (theta < 0 ? -sprite.radius : sprite.radius) * xPolar // ahem;
这里的工作示例:https://codepen.io/theprojectsomething/full/xadQvK/
注意当 phi 接近两极时的削波。在示例中还不错,但希望存在更优雅的解决方案,并且来自对作用力有更好理解的人!
备注:
- 心知肚明three.js
Spherical
class;手动计算是为了清楚起见。
- three.js示例中使用默认坐标space,任何可适应的解决方案都将被接受!
让我们尝试将我们拥有的东西形式化。首先,我们假设我们处于一个坐标系中,其中球体位于原点,视图方向为 z 轴。如果我们不在那个坐标系中,很容易将输入数据变换到这个坐标系中,然后进行计算,最后变换回原来的坐标系。
我们有一个方向向量 d
,它指定了我们希望精灵中心出现的方向(这就是您在代码片段中所说的 xyzPolar
)。此外,我们有精灵的宽度 w
和高度 h
并且我们知道宽度沿 x 轴扩展,高度沿 y 轴扩展(因为我们有一个视图对齐的坐标系).
现在,对于任意标量偏移 t
,我们可以将精灵的中心指定为 t * d
。然后我们的精灵上的点由以下集合描述:
{ t * d + x * (w/2, 0, 0) + y * (0, h/2, 0) | -1 <= x <= 1, -1 <= y <= 1 }
x
和 y
是精灵上的参数位置,其中 (-1, -1)
定义左下角,(0, 0)
定义中心。我们对最接近球体中心的点特别感兴趣,我们希望该点距离它 r
(球体半径)。因此:
min (t * dx + x * w/2)^2 + (t * dy + y * h/2)^2 + (t * dz)^2 = r^2
x, y in [-1, 1]
如果我们知道这个最近点的参数x
和y
,我们可以很容易地求解t
,得到精灵最终的中心位置。
但是,我们不知道这些参数。让我们把这个公式分开:
( min (t * dx + x * w/2)^2 ) + ( min (t * dy + y * h/2)^2 ) + (t * dz)^2 = r^2
x in [-1, 1] y in [-1, 1]
如果我们可以设置
,则前两项最小化
x = -2 dx t / w
y = -2 dy t / h
在这种情况下,两项都为零,我们可以求解 t = r / abs(dz)
。本质上,这会将精灵放在 z = +- r
的 xy 对齐平面上。如果我们有一个无限的精灵,并且我们没有限制 x
和 y
.
,这就是事实
但是,我们没有无限精灵。我们必须将 x
和 y
限制在允许的范围内。所以,如果我们有一个候选 t
,我们也可以通过简单地用上面的公式计算 x
和 y
并检查它们是否在允许的范围内来检查它是否是一个有效的解决方案.如果最近的点在精灵中间的某个地方(而不是在边缘或角落),这将是正确的。
幸运的是,我们需要检查 x
和 y
的几个可能值。因此,该算法将为所有可能的值计算 t
,然后检查解决方案是否有效,并且只保留单个有效解决方案。现在,x
和 y
的可能值是多少?
我们已经知道 -1 <= x <= 1
和 -1 <= y <= 1
的情况。此范围内的所有值都是等效的,因为它们使前两项为零(对最终结果没有影响)。然后每个变量还有两个案例。 x = -1
或 x = 1
(与 y
相同)。这给出了我们需要解决的总共 9 种组合。但我们可以做得更好。我们知道 t
、w
和 h
是正数。因此,x
和 y
的符号分别与 dx
和 dy
相反。例如,如果 dx
是正数,我们只需要检查 x = -1
或第一项消失的情况(本质上,这意味着如果方向矢量指向右侧)。等价地,如果 dx
或 dy
正好为零,我们立即知道相应的项消失了,我们不需要考虑其他情况。另外,如果dz = 0
,不评估前两项消失的情况
所以,我们只剩下四个案例了。作为参考,以下是每个变量的三种不同情况的术语:
first term
-1 = x dx^2 * t^2 - dx * t * w + w^2 / 4
-1 < x < 1 0
x = 1 dx^2 * t^2 + dx * t * w + w^2 / 4
second term
-1 = y dy^2 * t^2 - dy * t * h + h^2 / 4
-1 < y < 1 0
y = 1 dy^2 * t^2 + dy * t * h + h^2 / 4
对于您需要评估的四种情况,assemble 二次方程并求解 t
。最后,计算 x
和 y
并检查它们是否匹配大小写(如果你有 x = 1
的大小写,检查 x >= 1
等)最后,计算精灵中心为t * d
.
所以,不幸的是,这并不比你拥有的更优雅,但更准确。
给定一个 three.js 场景和 静态相机 ,一个 sphere 0,0,0
和任意尺寸的 矩形精灵 (例如文本标签),我正在寻找允许旋转精灵的 'threejs method' (或公式)围绕球体而不剪裁,以尽可能小的半径。
到目前为止,我的方法是计算球体上位置的极坐标,然后在精灵接近球体原点时将其偏移活动维度的一个因子。我已将其稍微调整为:
const xPolar = Math.sin(phi) * Math.sin(theta);
const yPolar = Math.cos(phi);
const zPolar = Math.sin(phi) * Math.cos(theta);
const x = xPolar + sprite.radius * xPolar;
const y = yPolar + sprite.radius * yPolar;
const z = zPolar + (theta < 0 ? -sprite.radius : sprite.radius) * xPolar // ahem;
这里的工作示例:https://codepen.io/theprojectsomething/full/xadQvK/
注意当 phi 接近两极时的削波。在示例中还不错,但希望存在更优雅的解决方案,并且来自对作用力有更好理解的人!
备注:
- 心知肚明three.js
Spherical
class;手动计算是为了清楚起见。 - three.js示例中使用默认坐标space,任何可适应的解决方案都将被接受!
让我们尝试将我们拥有的东西形式化。首先,我们假设我们处于一个坐标系中,其中球体位于原点,视图方向为 z 轴。如果我们不在那个坐标系中,很容易将输入数据变换到这个坐标系中,然后进行计算,最后变换回原来的坐标系。
我们有一个方向向量 d
,它指定了我们希望精灵中心出现的方向(这就是您在代码片段中所说的 xyzPolar
)。此外,我们有精灵的宽度 w
和高度 h
并且我们知道宽度沿 x 轴扩展,高度沿 y 轴扩展(因为我们有一个视图对齐的坐标系).
现在,对于任意标量偏移 t
,我们可以将精灵的中心指定为 t * d
。然后我们的精灵上的点由以下集合描述:
{ t * d + x * (w/2, 0, 0) + y * (0, h/2, 0) | -1 <= x <= 1, -1 <= y <= 1 }
x
和 y
是精灵上的参数位置,其中 (-1, -1)
定义左下角,(0, 0)
定义中心。我们对最接近球体中心的点特别感兴趣,我们希望该点距离它 r
(球体半径)。因此:
min (t * dx + x * w/2)^2 + (t * dy + y * h/2)^2 + (t * dz)^2 = r^2
x, y in [-1, 1]
如果我们知道这个最近点的参数x
和y
,我们可以很容易地求解t
,得到精灵最终的中心位置。
但是,我们不知道这些参数。让我们把这个公式分开:
( min (t * dx + x * w/2)^2 ) + ( min (t * dy + y * h/2)^2 ) + (t * dz)^2 = r^2
x in [-1, 1] y in [-1, 1]
如果我们可以设置
,则前两项最小化x = -2 dx t / w
y = -2 dy t / h
在这种情况下,两项都为零,我们可以求解 t = r / abs(dz)
。本质上,这会将精灵放在 z = +- r
的 xy 对齐平面上。如果我们有一个无限的精灵,并且我们没有限制 x
和 y
.
但是,我们没有无限精灵。我们必须将 x
和 y
限制在允许的范围内。所以,如果我们有一个候选 t
,我们也可以通过简单地用上面的公式计算 x
和 y
并检查它们是否在允许的范围内来检查它是否是一个有效的解决方案.如果最近的点在精灵中间的某个地方(而不是在边缘或角落),这将是正确的。
幸运的是,我们需要检查 x
和 y
的几个可能值。因此,该算法将为所有可能的值计算 t
,然后检查解决方案是否有效,并且只保留单个有效解决方案。现在,x
和 y
的可能值是多少?
我们已经知道 -1 <= x <= 1
和 -1 <= y <= 1
的情况。此范围内的所有值都是等效的,因为它们使前两项为零(对最终结果没有影响)。然后每个变量还有两个案例。 x = -1
或 x = 1
(与 y
相同)。这给出了我们需要解决的总共 9 种组合。但我们可以做得更好。我们知道 t
、w
和 h
是正数。因此,x
和 y
的符号分别与 dx
和 dy
相反。例如,如果 dx
是正数,我们只需要检查 x = -1
或第一项消失的情况(本质上,这意味着如果方向矢量指向右侧)。等价地,如果 dx
或 dy
正好为零,我们立即知道相应的项消失了,我们不需要考虑其他情况。另外,如果dz = 0
,不评估前两项消失的情况
所以,我们只剩下四个案例了。作为参考,以下是每个变量的三种不同情况的术语:
first term
-1 = x dx^2 * t^2 - dx * t * w + w^2 / 4
-1 < x < 1 0
x = 1 dx^2 * t^2 + dx * t * w + w^2 / 4
second term
-1 = y dy^2 * t^2 - dy * t * h + h^2 / 4
-1 < y < 1 0
y = 1 dy^2 * t^2 + dy * t * h + h^2 / 4
对于您需要评估的四种情况,assemble 二次方程并求解 t
。最后,计算 x
和 y
并检查它们是否匹配大小写(如果你有 x = 1
的大小写,检查 x >= 1
等)最后,计算精灵中心为t * d
.
所以,不幸的是,这并不比你拥有的更优雅,但更准确。