2015-11-26 77 views
0

我最近一直在試圖檢測玩家是否「瞄準」一個物體,但是我的探測有問題。Ray-Sphere Intersection

因此,這裏是我使用下式:

(ray * (origin-center))^2 - ||origin-center||^2 + r^2 >= 0 

其中射線是在玩家目標(源http://antongerdelan.net/opengl/raycasting.html),原點是照相機和中心的位置的方向是球的中心

我找到這個公式有https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection

這個翻譯成Java來

public boolean collideWithSphere(Vector3f c, Vector3f o, float r) { 
    float delta = 0; 
    Vector3f omc = Vector3f.sub(o, c); 
    float b = Vector3f.Dot(ray, omc); 
    delta = b*b-Vector3f.Dot(omc,omc)+r*r; 
    return delta >= 0; 
} 

我已經做了幾次測量,對於我來說,問題是ray * omc不是>=||omc||當從原點到球體的距離很大時,因爲沒有射線分量會精確地爲+ -1。

有人有辦法解決這個問題嗎?

感謝

====================================

編輯

好的,所以在一天結束時,我不得不倒轉omc的y分量以使其工作,我不清楚爲什麼,但我會處理它。

+0

你的'ray'有多長?這很重要,因爲你使用一個標量產品來處理從相機到球體的矢量。另外,你的'omc'應該是'cmo',因爲你想從攝影機獲取矢量到球體中心。 –

+0

射線是一個單位矢量。我想我已經嘗試了cmo的東西,但它沒有解決,但我可以再試一次。 – Nifil

+0

@MartinHennig光線的長度不是要求。你根據Origin的方向計算它;請參閱下面提供的代碼。 – Krythic

回答

0

對於我目前的遊戲引擎,我將這兩個類用於Ray-> Sphere相交測試。

BoundingSphere.cs

using System; 
using OpenTK; 

namespace DestinyEngine.Framework.Maths 
{ 
    public struct BoundingSphere : IEquatable<BoundingSphere> 
    { 

     /// <summary> 
     /// The sphere center. 
     /// </summary> 
     public readonly Vector3 Center; 

     /// <summary> 
     /// The sphere radius. 
     /// </summary> 
     public readonly float Radius; 

     /// <summary> 
     /// Constructs a bounding sphere with the specified center and radius. 
     /// </summary> 
     /// <param name="center">The sphere center.</param> 
     /// <param name="radius">The sphere radius.</param> 
     public BoundingSphere(Vector3 center , float radius) 
     { 
      this.Center = center; 
      this.Radius = radius; 
     } 

     /// <summary> 
     /// Test if a bounding box is fully inside, outside, or just intersecting the sphere. 
     /// </summary> 
     /// <param name="box">The box for testing.</param> 
     /// <returns>The containment type.</returns> 
     public ContainmentType Contains(BoundingBox box) 
     { 
      //check if all corner is in sphere 
      bool inside = true; 
      for(int index = 0; index < box.GetCorners().Length; index++) 
      { 
       Vector3 corner = box.GetCorners()[ index ]; 
       if(this.Contains(corner) == ContainmentType.Disjoint) 
       { 
        inside = false; 
        break; 
       } 
      } 
      if(inside) 
      { 
       return ContainmentType.Contains; 
      } 
      double dmin = 0; 
      if(Center.X < box.Min.X) 
      { 
       dmin += (Center.X - box.Min.X) * (Center.X - box.Min.X); 
      } 
      else if(Center.X > box.Max.X) 
      { 
       dmin += (Center.X - box.Max.X) * (Center.X - box.Max.X); 
      } 
      if(Center.Y < box.Min.Y) 
      { 
       dmin += (Center.Y - box.Min.Y) * (Center.Y - box.Min.Y); 
      } 
      else if(Center.Y > box.Max.Y) 
      { 
       dmin += (Center.Y - box.Max.Y) * (Center.Y - box.Max.Y); 
      } 
      if(Center.Z < box.Min.Z) 
      { 
       dmin += (Center.Z - box.Min.Z) * (Center.Z - box.Min.Z); 
      } 
      else if(Center.Z > box.Max.Z) 
      { 
       dmin += (Center.Z - box.Max.Z) * (Center.Z - box.Max.Z); 
      } 
      return dmin <= Radius * Radius ? ContainmentType.Intersects : ContainmentType.Disjoint; 
     } 

     /// <summary> 
     /// Test if a sphere is fully inside, outside, or just intersecting the sphere. 
     /// </summary> 
     /// <param name="sphere">The other sphere for testing.</param> 
     /// <returns>The containment type.</returns> 
     public ContainmentType Contains(BoundingSphere sphere) 
     { 
      float sqDistance = DestinyEngineMath.DistanceSquared(sphere.Center , Center); 
      if(sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius)) 
      { 
       return ContainmentType.Disjoint; 
      } 
      else if(sqDistance <= (Radius - sphere.Radius) * (Radius - sphere.Radius)) 
      { 
       return ContainmentType.Contains; 
      } 
      else 
      { 
       return ContainmentType.Intersects; 
      } 
     } 

     /// <summary> 
     /// Test if a point is fully inside, outside, or just intersecting the sphere. 
     /// </summary> 
     /// <param name="point">The vector in 3D-space for testing.</param> 
     /// <returns>The containment type.</returns> 
     public ContainmentType Contains(Vector3 point) 
     { 
      float sqRadius = Radius * Radius; 
      float sqDistance = DestinyEngineMath.DistanceSquared(point , Center); 
      if(sqDistance > sqRadius) 
      { 
       return ContainmentType.Disjoint; 
      } 
      else if(sqDistance < sqRadius) 
      { 
       return ContainmentType.Contains; 
      } 
      else 
      { 
       return ContainmentType.Intersects; 
      } 
     } 

     /// <summary> 
     /// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingBox"/>. 
     /// </summary> 
     /// <param name="box">The box to create the sphere from.</param> 
     /// <returns>The new <see cref="BoundingSphere"/>.</returns> 
     public static BoundingSphere CreateFromBoundingBox(BoundingBox box) 
     { 
      Vector3 center = new Vector3(
       (box.Min.X + box.Max.X)/2.0f , 
       (box.Min.Y + box.Max.Y)/2.0f , 
       (box.Min.Z + box.Max.Z)/2.0f); 
      float radius = DestinyEngineMath.Distance(center , box.Max); 
      return new BoundingSphere(center , radius); 
     } 


     /// <summary> 
     /// Compares whether current instance is equal to specified <see cref="BoundingSphere"/>. 
     /// </summary> 
     /// <param name="other">The <see cref="BoundingSphere"/> to compare.</param> 
     /// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns> 
     public bool Equals(BoundingSphere other) 
     { 
      return this.Center == other.Center && this.Radius == other.Radius; 
     } 

     /// <summary> 
     /// Compares whether current instance is equal to specified <see cref="Object"/>. 
     /// </summary> 
     /// <param name="obj">The <see cref="Object"/> to compare.</param> 
     /// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns> 
     public override bool Equals(object obj) 
     { 
      if(obj is BoundingSphere) 
      { 
       return this.Equals((BoundingSphere)obj); 
      } 
      return false; 
     } 

     /// <summary> 
     /// Gets the hash code of this <see cref="BoundingSphere"/>. 
     /// </summary> 
     /// <returns>Hash code of this <see cref="BoundingSphere"/>.</returns> 
     public override int GetHashCode() 
     { 
      return this.Center.GetHashCode() + this.Radius.GetHashCode(); 
     } 

     /// <summary> 
     /// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this sphere. 
     /// </summary> 
     /// <param name="box">The box for testing.</param> 
     /// <returns><c>true</c> if <see cref="BoundingBox"/> intersects with this sphere; <c>false</c> otherwise.</returns> 
     public bool Intersects(BoundingBox box) 
     { 
      return box.Intersects(this); 
     } 

     /// <summary> 
     /// Gets whether or not the other <see cref="BoundingSphere"/> intersects with this sphere. 
     /// </summary> 
     /// <param name="sphere">The other sphere for testing.</param> 
     /// <returns><c>true</c> if other <see cref="BoundingSphere"/> intersects with this sphere; <c>false</c> otherwise.</returns> 
     public bool Intersects(BoundingSphere sphere) 
     { 
      float sqDistance = DestinyEngineMath.DistanceSquared(sphere.Center , Center); 
      return !(sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius)); 
     } 

     /// <summary> 
     /// Returns a <see cref="String"/> representation of this <see cref="BoundingSphere"/> in the format: 
     /// {Center:[<see cref="Center"/>] Radius:[<see cref="Radius"/>]} 
     /// </summary> 
     /// <returns>A <see cref="String"/> representation of this <see cref="BoundingSphere"/>.</returns> 
     public override string ToString() 
     { 
      return "{Center:" + this.Center + " Radius:" + this.Radius + "}"; 
     } 

     /// <summary> 
     /// Compares whether two <see cref="BoundingSphere"/> instances are equal. 
     /// </summary> 
     /// <param name="a"><see cref="BoundingSphere"/> instance on the left of the equal sign.</param> 
     /// <param name="b"><see cref="BoundingSphere"/> instance on the right of the equal sign.</param> 
     /// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns> 
     public static bool operator ==(BoundingSphere a , BoundingSphere b) 
     { 
      return a.Equals(b); 
     } 

     /// <summary> 
     /// Compares whether two <see cref="BoundingSphere"/> instances are not equal. 
     /// </summary> 
     /// <param name="a"><see cref="BoundingSphere"/> instance on the left of the not equal sign.</param> 
     /// <param name="b"><see cref="BoundingSphere"/> instance on the right of the not equal sign.</param> 
     /// <returns><c>true</c> if the instances are not equal; <c>false</c> otherwise.</returns> 
     public static bool operator !=(BoundingSphere a , BoundingSphere b) 
     { 
      return !a.Equals(b); 
     } 
    } 
} 

Ray.cs

using System; 
using OpenTK; 
using OpenTK.Graphics.OpenGL; 

namespace DestinyEngine.Framework.Maths 
{ 
    public struct Ray : IEquatable<Ray> 
    { 
     public readonly Vector3 Origin; 
     public readonly Vector3 Direction; 

     public Ray(Vector3 origin , Vector3 direction) 
     { 
      this.Origin = origin; 
      this.Direction = direction; 
     } 

     public Ray(float originX , float originY , float originZ , float directionX , float directionY , float directionZ) 
     { 
      this.Origin = new Vector3(originX , originY , originZ); 
      this.Direction = new Vector3(directionX , directionY , directionZ); 
     } 

     public override bool Equals(object obj) 
     { 
      return (obj is Ray) && this.Equals((Ray)obj); 
     } 

     public bool Equals(Ray other) 
     { 
      return this.Origin.Equals(other.Origin) && this.Direction.Equals(other.Direction); 
     } 

     public override int GetHashCode() 
     { 
      return Origin.GetHashCode()^Direction.GetHashCode(); 
     } 

     public float? Intersects(BoundingBox box) 
     { 
      const float epsilon = 1e-6f; 
      float? tMin = null, tMax = null; 
      if(Math.Abs(Direction.X) < epsilon) 
      { 
       if(Origin.X < box.Min.X || Origin.X > box.Max.X) 
       { 
        return null; 
       } 
      } 
      else 
      { 
       tMin = (box.Min.X - Origin.X)/Direction.X; 
       tMax = (box.Max.X - Origin.X)/Direction.X; 
       if(tMin > tMax) 
       { 
        var temp = tMin; 
        tMin = tMax; 
        tMax = temp; 
       } 
      } 
      if(Math.Abs(Direction.Y) < epsilon) 
      { 
       if(Origin.Y < box.Min.Y || Origin.Y > box.Max.Y) 
       { 
        return null; 
       } 
      } 
      else 
      { 
       float tMinY = (box.Min.Y - Origin.Y)/Direction.Y; 
       float tMaxY = (box.Max.Y - Origin.Y)/Direction.Y; 
       if(tMinY > tMaxY) 
       { 
        float temp = tMinY; 
        tMinY = tMaxY; 
        tMaxY = temp; 
       } 
       if((tMin.HasValue && tMin > tMaxY) || (tMax.HasValue && tMinY > tMax)) 
       { 
        return null; 
       } 
       if(!tMin.HasValue || tMinY > tMin) 
       { 
        tMin = tMinY; 
       } 
       if(!tMax.HasValue || tMaxY < tMax) 
       { 
        tMax = tMaxY; 
       } 
      } 
      if(Math.Abs(Direction.Z) < epsilon) 
      { 
       if(Origin.Z < box.Min.Z || Origin.Z > box.Max.Z) 
       { 
        return null; 
       } 
      } 
      else 
      { 
       float tMinZ = (box.Min.Z - Origin.Z)/Direction.Z; 
       float tMaxZ = (box.Max.Z - Origin.Z)/Direction.Z; 
       if(tMinZ > tMaxZ) 
       { 
        float temp = tMinZ; 
        tMinZ = tMaxZ; 
        tMaxZ = temp; 
       } 
       if((tMin.HasValue && tMin > tMaxZ) || (tMax.HasValue && tMinZ > tMax)) 
       { 
        return null; 
       } 
       if(!tMin.HasValue || tMinZ > tMin) 
       { 
        tMin = tMinZ; 
       } 
       if(!tMax.HasValue || tMaxZ < tMax) 
       { 
        tMax = tMaxZ; 
       } 
      } 
      if((tMin.HasValue && tMin < 0) && tMax > 0) 
      { 
       return 0; 
      } 
      return tMin < 0 ? null : tMin; 
     } 

     public float? Intersects(BoundingSphere sphere) 
     { 
      Vector3 difference = sphere.Center - this.Origin; 
      float differenceLengthSquared = DestinyEngineMath.LengthSquared(difference); 
      float sphereRadiusSquared = sphere.Radius * sphere.Radius; 
      float distanceAlongRay; 
      if(differenceLengthSquared < sphereRadiusSquared) 
      { 
       return 0.0f; 
      } 
      Vector3 refDirection = this.Direction; 
      Vector3.Dot(ref refDirection , ref difference , out distanceAlongRay); 
      if(distanceAlongRay < 0) 
      { 
       return null; 
      } 
      float dist = sphereRadiusSquared + distanceAlongRay * distanceAlongRay - differenceLengthSquared; 
      return (dist < 0) ? null : distanceAlongRay - (float?)Math.Sqrt(dist); 
     } 

     public static bool operator !=(Ray a , Ray b) 
     { 
      return !(a.Origin.Equals(b.Origin) && a.Direction.Equals(b.Direction)); 
     } 

     public static bool operator ==(Ray a , Ray b) 
     { 
      return a.Origin.Equals(b.Origin) && a.Direction.Equals(b.Direction); 
     } 

     public override string ToString() 
     { 
      return "{{Origin:" + Origin + " Direction:" + Direction + "}}"; 
     } 

     public void DrawImmediate() 
     { 
      Vector3 end = this.Origin + this.Direction * 400.0f; 
      GL.Begin(PrimitiveType.Lines); 
      GL.Vertex3(this.Origin); 
      GL.Vertex3(end); 
      GL.End(); 
     } 
    } 
} 

使用這些,你可以這樣做:

Ray ray = new Ray(new Vector3(0, 0, 0), new Vector3(1, 1, 1)); 
      BoundingSphere sphere = new BoundingSphere(new Vector3(1,1,1), 0.5f); 
      Console.WriteLine(ray.Intersects(sphere)); // Gives length until intersection. 

上面的代碼是用C#編寫,它可以很容易地被移植到Java。但是,我不知道爲什麼有人會使用Java;這是一種可怕的語言。

+0

謝謝,明天我會試試 – Nifil

+0

@Nifil沒問題!祝你的項目好運! – Krythic

-1

您的ray必須是向量的確切長度cmo。所以,如果你是ray歸,你必須

ray = ray * lengthOfCmO 

所以,把你的相機球矢量身邊,讓你的光,以正確的長度和你的函數應該工作。你應該注意,這個公式在數學上是不正確的。您只需檢查光線在相機球面向量上的投影是否比球面相機的長度減去球體半徑。它會起作用,但你必須使用比你想要被射線擊中的更大的球體。

+0

請留下評論爲什麼你downvoted。 –

+1

您對當前算法的假設和建議完全錯誤。 – Krythic

相關問題