سؤال

So basicly I want to reflect a ray over a triangle. Here's my ray class

public sealed class Ray
{
    public readonly Point3D Source;
    public readonly Point3D Direction;

    public readonly Color Light;

    public Ray(Point3D source, Point3D direction, Color light)
    {
        if (source == direction)
        {
            throw new ArgumentException("Source and Direction cannot be equal");
        }

        this.Source = source;
        this.Direction = direction;

        this.Light = light;
    }
}

Heres my Point3D class

public struct Point3D : IEquatable<Point3D>
{
    public static readonly Point3D Zero = new Point3D();

    public float X;
    public float Y;
    public float Z;

    public Point3D(float x, float y, float z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public override bool Equals(object obj)
    {
        if (!(obj is Point3D))
        {
            return false;
        }

        return this.Equals((Point3D)obj);
    }

    public static bool operator ==(Point3D one, Point3D two)
    {
        return one.Equals(two);
    }
    public static bool operator !=(Point3D one, Point3D two)
    {
        return !one.Equals(two);
    }

    public static Point3D operator *(float n, Point3D v)
    {
        return new Point3D(v.X * n, v.Y * n, v.Z * n);
    }
    public static Point3D operator +(Point3D v1, Point3D v2)
    {
        return new Point3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
    }
    public static Point3D operator -(Point3D v1, Point3D v2)
    {
        return new Point3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
    }

    public static float operator *(Point3D v1, Point3D v2)
    {
        return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
    }
    public static float Magnitude(Point3D v)
    {
        return (float)Math.Sqrt(v * v);
    }

    public static Point3D Normalize(Point3D v)
    {
        float mag = Magnitude(v);
        float div = (mag == 0) ? float.PositiveInfinity : (1 / mag);
        return div * v;
    }
    public static Point3D Cross(Point3D v1, Point3D v2)
    {
        return new Point3D(((v1.Y * v2.Z) - (v1.Z * v2.Y)),
                          ((v1.Z * v2.X) - (v1.X * v2.Z)),
                          ((v1.X * v2.Y) - (v1.Y * v2.X)));
    }

    /// <summary>
    ///  doesnt take square root
    /// </summary>
    public static float FastDistance(Point3D v1, Point3D v2)
    {
        float x = v1.X - v2.X;
        x *= x;


        float y = v1.Y - v2.Y;
        y *= y;


        float z = v1.Z - v2.Z;
        z *= z;

        return x + y + z;
    }
    /// <summary>
    /// Takes square root:
    /// </summary>
    public static float Distance(Point3D v1, Point3D v2)
    {
        return (float)Math.Sqrt(Point3D.FastDistance(v1, v2));
    }

    public override int GetHashCode()
    {
        return this.X.GetHashCode()
            ^ this.Y.GetHashCode()
            ^ this.Y.GetHashCode();
    }

    public override string ToString()
    {
        return this.X + ", " + this.Y + ", " + this.Z;
    }

    public bool Equals(Point3D other)
    {
        return this.X == other.X
            && this.Y == other.Y
            && this.Z == other.Z;
    }
}

and finally here is where I need my method to be realized.

public interface ITriangleAccess
{
    Triangle3D Find(Ray ray, out Point3D crossPoint);
}

public sealed class TriangleAccess : ITriangleAccess
{
    private readonly List<KeyValuePair<float, Triangle3D>> trianglesByX;

    private readonly List<Triangle3D> allTriangles;

    public TriangleAccess(Body[] bodies)
    {
        if (null == bodies)
        {
            throw new ArgumentNullException("bodies");
        }

        this.allTriangles = bodies.SelectMany((x) => x.Parts).ToList();

        this.trianglesByX = bodies.SelectMany((x) => x.Parts).SelectMany((y) => new KeyValuePair<float, Triangle3D>[]
        {
            new KeyValuePair<float,Triangle3D>(y.Point1.X,y),
            new KeyValuePair<float,Triangle3D>(y.Point2.X,y),
            new KeyValuePair<float,Triangle3D>(y.Point3.X,y)
        }).ToList();
    }

    public Triangle3D Find(Ray ray, out Point3D crossPoint)
    {
        crossPoint = Point3D.Zero;

        List<Triangle3D> relevant = this.GetRelevantTriangles(ray);

        Triangle3D absoluteTriangle = null;
        float min = float.MaxValue;

        foreach (Triangle3D item in relevant)
        {
            Point3D currentCrossPoint;
            if (this.RayIntersectTriangle(ray, item, out currentCrossPoint))
            {
                float distance = Point3D.Distance(ray.Source, currentCrossPoint);
                if (distance < min)
                {
                    absoluteTriangle = item;
                    crossPoint = currentCrossPoint;
                    min = distance;
                }
            }
        }

        return absoluteTriangle;
    }
    public Ray Reflect(Ray ray, Point3D crossPoint, Triangle3D intersect)
    {
        //need this to be realized
        //please help
    }

    /// <summary>
    /// TODO: Finish this Up:
    /// </summary>
    /// <param name="ray"></param>
    /// <returns></returns>
    private List<Triangle3D> GetRelevantTriangles(Ray ray)
    {
        return this.allTriangles;
    }

    private bool RayIntersectTriangle(Ray ray, Triangle3D triangle, out Point3D crossPoint)
    {
        // Find vectors for two edges sharing vert0
        Point3D edge1 = triangle.Point2 - triangle.Point1;
        Point3D edge2 = triangle.Point3 - triangle.Point1;

        // Begin calculating determinant - also used to calculate barycentricU parameter
        Point3D pvec = Point3D.Cross(ray.Direction, edge2);

        // If determinant is near zero, ray lies in plane of triangle
        float det = edge1 * pvec;
        if (det < 0.0001f)
        {
            crossPoint = Point3D.Zero;
            return false;
        }

        // Calculate distance from vert0 to ray origin
        Point3D tvec = ray.Source - triangle.Point1;

        // Calculate barycentricU parameter and test bounds
        float barycentricU = tvec * pvec;
        if (barycentricU < 0.0f || barycentricU > det)
        {
            crossPoint = Point3D.Zero;
            return false;
        }

        // Prepare to test barycentricV parameter
        Point3D qvec = Point3D.Cross(tvec, edge1);

        // Calculate barycentricV parameter and test bounds
        float barycentricV = ray.Direction * qvec;
        if (barycentricV < 0.0f || barycentricU + barycentricV > det)
        {
            crossPoint = Point3D.Zero;
            return false;
        }

        // Calculate pickDistance, scale parameters, ray intersects triangle
        float pickDistance = edge2 * qvec;
        float fInvDet = 1.0f / det;


        pickDistance *= fInvDet;
        barycentricU *= fInvDet;
        barycentricV *= fInvDet;

        crossPoint = MathHelper.BaryCentric(triangle, barycentricU, barycentricV);
        return true;
    }

}

Thank you. P.S have patience with me... I'm just 15 :)

هل كانت مفيدة؟

المحلول

I'm assuming that if you have a ray that intersects with a triangle, your Reflect function should return a ray that originates at that intersection and heads off in a new direction (like light on a mirror).

That said, we already have the ray source (crossPoint).

The formula for the reflected vector (direction of the ray) is R=V−2N(V⋅N) where V is the inverted direction of the incoming ray (as if it originated from the object) and N is the normal vector of the triangle.

With your code, you should just need to something like:

public Ray Reflect(Ray ray, Point3D crossPoint, Triangle3D intersect)
{
    Point3D V = -ray.Direction.Normalize();
    return new Ray(crossPoint, 
                   V - 2 * intersect.Normal * (V * intersect.Normal), 
                   Color.white);
}

Also, if your triangle structure doesn't have the normal, you calculate it using the cross product of the vectors that form each side.

نصائح أخرى

public Ray Reflect(Ray ray, Point3D crossPoint, Triangle3D intersect)
{
  // find normal of intersect triangle

  Point3D normal = Point3D.Cross( intersect.Point2 - intersect.Point1,
                                  intersect.Point3 - intersect.Point1 );

  normal = Point3D.Normalize( normal );

  // find ray part before intersection

  Point3D inbound = crossPoint - ray.Source;

  // find projection of inbound ray to normal

  Point3D projection = (normal * inbound) * normal;

  // find lateral component of inbound ray

  Point3D lateral = inbound - projection;

  // find outbound direction

  Point3D direction = (ray.Source + 2 * lateral) - crossPoint;

  direction = Point3D.Normalize( direction );

  // I assume your direction is unit vector (magnitude = 1)
  // if not multiply it with magnitude you want to be

  // direction = ... * direction;

  return new Ray( crossPoint, direction, ray.Color );
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top