我正在拍摄一个三维球面上的光线投射,我想要一种方法,它可以根据光线点(位置)从0(喷流中心)到1(圆周或球体轮廓)之间的浮点数。我怎样才能做到这一点。
我试着计算了球体位置和射线投落点之间的差异,虽然有点工作,但在代码中实现起来非常复杂。
还有其他方法吗?还是我遗漏了一些数学函数或向量函数,在这种情况下是有用的。
发布于 2022-03-25 08:38:21
你可以取一个垂直于射线方向的Plane,穿过球体的起源,然后像这样使用Plane.Raycast将命中位置投射到球体上。
...
if(Physics.Raycast(ray, out var hit))
{
if(hit.collider is SphereCollider hitSphere)
{
// This is assuming that your sphere is actually a sphere with uniform local scales
var sphereRadius = hitSphere.radius * hitSphere.tranform.lossyScale.x;
var hitObjectCenter = hit.collider.bounds.center;
var plane = new Plane(-ray.diretcion, hitObjectCenter);
if(plane.Raycast(ray, out var hitDistance))
{
var planeHitPoint = ray.GetPoint(hitDistance);
var result = Vector3.Distance(hitObjectCenter, planeHitPoint) / sphereRadius;
// now result should be your value between 0 (sphere center) and 1 (exactly on the sphere "edge" - probably never fully reached of course)
}
}
}由于我们的平面使用的射线方向是正常的,你也可以用Plane.ClosestPointOnPlane近似。
var plane = new Plane(-ray.diretcion, hitObjectCenter);
var planeHitPoint = plane.ClosestPointOnPlane(hitDistance);
var result = Vector3.Distance(hitObjectCenter, planeHitPoint) / sphereRadius;结果应该是几乎相同的,不确定哪一个更快,但我想光线投射是更精确的(我们说的差别有点像0.000001 ;)。
作为一个小演示,因为解释这是如何工作的有点复杂^^

public class Example : MonoBehaviour
{
public Camera mainCamera;
private float result;
private Ray ray;
private Vector3 hitPoint;
private Vector3? hitObjectCenter;
private Vector3 planeHitPoint;
private void Awake()
{
if (!mainCamera) mainCamera = Camera.main;
}
private void Update()
{
ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out var hit) && hit.collider is SphereCollider hitSphere)
{
hitPoint = hit.point;
// This is assuming that your sphere is actually a sphere with uniform local scales
var sphereRadius = hitSphere.radius * hitSphere.transform.lossyScale.x;
hitObjectCenter = hit.collider.bounds.center;
var plane = new Plane(-ray.direction, hitObjectCenter.Value);
if(plane.Raycast(ray, out var hitDistance))
{
var planeHitPoint = ray.GetPoint(hitDistance);
var result = Vector3.Distance(hitObjectCenter, planeHitPoint) / sphereRadius;
}
}
else
{
hitObjectCenter = null;
}
}
private Mesh planeMesh;
private void OnDrawGizmos()
{
// if not hitting a sphere do nothing
if (!hitObjectCenter.HasValue)
{
return;
}
// lazy initialize plan visualization
if (!planeMesh)
{
planeMesh = new Mesh
{
vertices = new Vector3[4]
{
new Vector3(-1, -1),
new Vector3(-1, 1),
new Vector3(1, 1),
new Vector3(1, -1)
},
triangles = new[]
{
0, 1, 2,
0, 2, 3,
// backfaces
0, 2, 1,
0, 3, 2
},
normals = new Vector3[4]
{
Vector3.forward,
Vector3.forward,
Vector3.forward,
Vector3.forward,
}
};
}
// Visualize ray
Gizmos.color = Color.green;
Gizmos.DrawLine(ray.origin, hitPoint);
// Visualize hit point on sphere
Gizmos.DrawWireSphere(hitPoint, 0.05f);
// Visualize Plane
Gizmos.color = Color.cyan;
Gizmos.DrawWireMesh(planeMesh, hitObjectCenter.Value, Quaternion.LookRotation(ray.direction));
// visualize hit point on plane
Gizmos.DrawWireSphere(planeHitPoint, 0.05f);
// Visualize distance
Gizmos.color = Color.red;
Gizmos.DrawLine(planeHitPoint, hitObjectCenter.Value);
// print result on screen
Handles.Label((planeHitPoint + hitObjectCenter.Value) / 2f, $"RESULT: {result:0.000}", new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter });
}
}https://stackoverflow.com/questions/71613725
复制相似问题