射线追踪C#三角交叉口

时间:2013-07-19 21:57:34

标签: c# .net geometry linear-algebra raytracing

基本上我想在三角形上反射一条光线。这是我的光线类

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;
    }
}

继承我的Point3D课程

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;
    }
}

最后这里是我需要实现的方法。

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;
    }

}

谢谢。 P.S对我有耐心......我只是15:)

2 个答案:

答案 0 :(得分:1)

我假设如果你有一条与三角形相交的光线,你的反射函数应该返回一条源自该交叉点的光线,然后向新的方向移动(就像镜子上的光线)。

那就是说,我们已经拥有射线源(crossPoint)。

反射矢量的公式(光线方向)是R = V-2N(V⋅N),其中V是入射光线的反转方向(好像它来自物体),N是法线矢量三角形。

使用您的代码,您应该只需要:

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);
}

另外,如果你的三角形结构没有法线,你可以用形成每一边的向量的叉积来计算它。

答案 1 :(得分:1)

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 );
}