与三角形的背面碰撞时如何忽略相机的碰撞?

时间:2020-07-25 01:09:17

标签: c# directx collision-detection

我想知道是否有人可以启发我,当照相机与三角形的背面碰撞时如何忽略照相机碰撞。我想实现这一点,以使我或玩家以某种方式发现自己在模型中时不会卡住我的相机。这是我的摄像头系统的视频:

A Video My Camera System

以下是该碰撞检测的代码:

private void CheckKeyboardInput(float deltaTime)
        {
            Vector3 previousCameraLocation = Camera.Location;
            KeyboardState state = Keyboard.GetCurrentState();
            Camera.IsAccelerating = state.IsPressed(Key.LeftShift);
            bool isCameraMoving = false;
            if (state.IsPressed(Key.W))
            {
                Camera.TranslateForward(deltaTime);
                isCameraMoving = true;
            }
            if (state.IsPressed(Key.S))
            {
                Camera.TranslateBackward(deltaTime);
                isCameraMoving = true;
            }
            if (state.IsPressed(Key.A))
            {
                Camera.TranslateLeft(deltaTime);
                isCameraMoving = true;
            }
            if (state.IsPressed(Key.D))
            {
                Camera.TranslateRight(deltaTime);
                isCameraMoving = true;
            }
            if (state.IsPressed(Key.E))
            {
                Camera.TranslateUp(deltaTime);
                isCameraMoving = true;
            }
            if (state.IsPressed(Key.Q))
            {
                Camera.TranslateDown(deltaTime);
                isCameraMoving = true;
            }
            /**
             * Process Camera Collisions
             */
            if (Camera.IsCollisionEnabled)
            {
                if (isCameraMoving)
                {
                    Vector3 currentCameraLocation = this.Camera.Location;
                    int totalCollisions = 0;
                    float finalCameraLocationX = Camera.Location.X;
                    float finalCameraLocationY = Camera.Location.Y;
                    float finalCameraLocationZ = Camera.Location.Z;
                    foreach (StaticGeometry geometry in this.StaticGeometry)
                    {
                        if (geometry.IsCameraCollidable)
                        {
                            if (Vector3.Distance(geometry.Location, Camera.Location) <= Camera.CollisionDistance)
                            {
                                for (int i = 0; i < geometry.Mesh.Vertices.Length; i += 3)
                                {
                                    // Get the Vertex Positions for the current Triangle
                                    Vector3 position1 = geometry.Mesh.Vertices[i].Location;
                                    Vector3 position2 = geometry.Mesh.Vertices[i + 1].Location;
                                    Vector3 position3 = geometry.Mesh.Vertices[i + 2].Location;

                                    // Create the rotation matrix using the geometry's current rotation setting.
                                    Matrix rotationMatrix = VoidwalkerMath.CreateRotationMatrix(geometry.Rotation);

                                    // Transform the Coordinate with the Rotation Matrix, then add the geometry's location
                                    Vector3 finalVertexLocation1 = Vector3.TransformCoordinate(position1, rotationMatrix) + geometry.Location;
                                    Vector3 finalVertexLocation2 = Vector3.TransformCoordinate(position2, rotationMatrix) + geometry.Location;
                                    Vector3 finalVertexLocation3 = Vector3.TransformCoordinate(position3, rotationMatrix) + geometry.Location;

                                    Vector3 translationX = new Vector3(currentCameraLocation.X, previousCameraLocation.Y, previousCameraLocation.Z);
                                    Vector3 translationY = new Vector3(previousCameraLocation.X, currentCameraLocation.Y, previousCameraLocation.Z);
                                    Vector3 translationZ = new Vector3(previousCameraLocation.X, previousCameraLocation.Y, currentCameraLocation.Z);
                                    // Test X
                                    BoundingSphere sphereX = new BoundingSphere(translationX, Camera.HardBoundsRadius);
                                    if (sphereX.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
                                    {
                                        finalCameraLocationX = previousCameraLocation.X;
                                        totalCollisions++;
                                    }
                                    // Test Y
                                    BoundingSphere sphereY = new BoundingSphere(translationY, Camera.HardBoundsRadius);
                                    if (sphereY.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
                                    {
                                        finalCameraLocationY = previousCameraLocation.Y;
                                        totalCollisions++;
                                    }
                                    // Test Z
                                    BoundingSphere sphereZ = new BoundingSphere(translationZ, Camera.HardBoundsRadius);
                                    if (sphereZ.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
                                    {
                                        finalCameraLocationZ = previousCameraLocation.Z;
                                        totalCollisions++;
                                    }

                                    /**
                                     * Edge case early termination. If the camera is already colliding
                                     * on all axis, we don't need to test for any further collisions. This seems
                                     * to only trigger when the player is wedged in a corner.
                                     */
                                    if (totalCollisions > 0) // Might not need this.
                                    {
                                        if (
                                            finalCameraLocationX == previousCameraLocation.X &&
                                            finalCameraLocationY == previousCameraLocation.Y &&
                                            finalCameraLocationZ == previousCameraLocation.Z)
                                        {
                                            this.Camera.Location = new Vector3(
                                                finalCameraLocationX,
                                                finalCameraLocationY,
                                                finalCameraLocationZ);
                                            return;
                                        }
                                    }

                                }
                            }
                        }
                    }
                    this.Camera.Location = new Vector3(finalCameraLocationX, finalCameraLocationY, finalCameraLocationZ);
                }
            }
        }

还有我的Vertex类,只是向大家展示我如何设置它:

using SharpDX;
using System.Runtime.InteropServices;

namespace VoidwalkerEngine.Framework.DirectX.Rendering
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Vertex
    {

        public static Vertex Zero = new Vertex(Vector3.Zero,Vector2.Zero,Vector3.Zero);

        public Vector3 Location;
        public Vector2 TexCoords;
        public Vector3 Normal;

        public const int Size = 32;


        public Vertex(Vector3 position, Vector2 texCoords, Vector3 normal)
        {
            this.Location = position;
            this.Normal = normal;
            this.TexCoords = texCoords;
        }

        public Vertex(Vertex other)
        {
            this.Location = other.Location;
            this.Normal = other.Normal;
            this.TexCoords = other.TexCoords;
        }

        public override string ToString()
        {
            return
                "Location: " + Location.ToString() +
                ", TexCoords: " + TexCoords.ToString() +
                ", Normal: " + Normal.ToString();
        }

    }
}

已更新:摄像机类别

using SharpDX;
using System;

namespace VoidwalkerEngine.Framework.DirectX
{
    public enum CameraMode
    {
        FreeLook,
        Orbit
    }

    public class Camera
    {
        /// <summary>
        /// The name of this camera
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// The eye location of this camera
        /// </summary>
        public Vector3 Location { get; set; }
        /// <summary>
        /// The Pitch of this Camera, as Radians
        /// </summary>
        public float Pitch { get; set; }
        /// <summary>
        /// The Yaw of this Camera, as Radians
        /// </summary>
        public float Yaw { get; set; }
        /// <summary>
        /// The Roll of this Camera, as Radians
        /// </summary>
        public float Roll { get; set; }
        /// <summary>
        /// The NearZ of this Camera
        /// </summary>
        public float NearZ { get; set; }
        /// <summary>
        /// The FarZ of this Camera
        /// </summary>
        public float FarZ { get; set; }
        /// <summary>
        /// The Field of View of this Camera, value should be
        /// between 0.70 and 1.20
        /// </summary>
        public float FieldOfView { get; set; }
        public float AspectRatio { get; set; }
        public float LookSpeed { get; set; }
        public float MoveSpeed { get; set; }
        /// <summary>
        /// Determines if this Camera is currently accelerating.
        /// </summary>
        public bool IsAccelerating { get; set; }
        /// <summary>
        /// The acceleration speed multiplier of this Camera.
        /// </summary>
        public float AccelerationMultiplier { get; set; }
        public CameraMode Mode { get; set; }
        public float ViewportWidth;
        public float ViewportHeight;
        public float HardBoundsRadius { get; set; } = 2.0f;
        public float CollisionDistance { get; set; } = 128;
        public bool IsCollisionEnabled { get; set; } = true;

        /// <summary>
        /// The BoundingSphere of this Camera
        /// </summary>
        public BoundingSphere HardBounds
        {
            get
            {
                return new BoundingSphere()
                {
                    Center = this.Location,
                    Radius = HardBoundsRadius
                };
            }
        }

        /// <summary>
        /// The Target Vector of this Camera
        /// </summary>
        public Vector3 Target
        {
            get
            {
                return new Vector3(
                    (float)Math.Sin(this.Yaw),
                    (float)Math.Tan(this.Pitch),
                    (float)Math.Cos(this.Yaw));
            }
        }

        /// <summary>
        /// The Frustum of this Camera
        /// </summary>
        public BoundingFrustum Frustum
        {
            get
            {
                return new BoundingFrustum(this.ModelViewProjectionMatrix);
            }
        }

        public Matrix ModelViewMatrix
        {
            get
            {
                return Matrix.LookAtLH(this.Location, Location + Target, Up);
            }
        }

        public Matrix ProjectionMatrix
        {
            get
            {
                return Matrix.PerspectiveFovRH(FieldOfView, AspectRatio, NearZ, FarZ);
            }
        }

        public Matrix ModelViewProjectionMatrix
        {
            get
            {
                return ModelViewMatrix * ProjectionMatrix;
            }
        }

        //public CardinalDirectionType Direction
        //{
        //    get
        //    {
        //        return VoidwalkerMath.GetCardinalDirection(VoidwalkerMath.ToDegrees(Yaw));
        //    }
        //}

        public Vector3 Forward
        {
            get
            {
                return new Vector3((float)Math.Cos(Pitch), 0, (float)Math.Sin(Pitch));
            }
        }

        public Vector3 Right
        {
            get
            {
                return new Vector3(Forward.Z, 0, -Forward.Z);
            }
        }

        public Vector3 Up
        {
            get
            {
                return new Vector3(-(float)Math.Sin(Roll), (float)Math.Cos(Roll), 0);
            }
        }

        public Camera()
        {

        }

        public Camera(string name)
            : this()
        {
            this.Name = name;
            this.Location = new Vector3();
        }

        public void ToOrigin()
        {
            Transform(Vector3.Zero, 0, 0, 0);
        }

        public void Transform(Vector3 location, float pitch, float yaw, float roll)
        {
            this.Location = location;
            this.Pitch = pitch;
            this.Yaw = yaw;
            this.Roll = roll;
        }

        public float GetCurrentMoveSpeed()
        {
            if (IsAccelerating)
            {
                return this.MoveSpeed * this.AccelerationMultiplier;
            }
            return this.MoveSpeed;
        }

        public void TranslateLeft(float deltaTime)
        {
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                Location.X - (float)Math.Sin(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime,
                Location.Y,
                Location.Z - (float)Math.Cos(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime);
        }

        public void TranslateRight(float deltaTime)
        {
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                Location.X + (float)Math.Sin(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime,
                Location.Y,
                Location.Z + (float)Math.Cos(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime);
        }

        public void TranslateForward(float deltaTime)
        {
            float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
            float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                this.Location.X - (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
                this.Location.Y - (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
                this.Location.Z - (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
        }

        public void TranslateBackward(float deltaTime)
        {

            float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
            float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                this.Location.X + (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
                this.Location.Y + (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
                this.Location.Z + (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
        }

        public void TransformYawPitch(float dx, float dy)
        {
            Yaw -= dx * LookSpeed;
            Pitch += dy * LookSpeed;
            const float pitchClamp = 1.56f;
            if (Pitch <= -pitchClamp)
            {
                Pitch = -pitchClamp;
            }
            if (Pitch >= pitchClamp)
            {
                Pitch = pitchClamp;
            }
        }

        public void TranslateUp(float deltaTime)
        {
            this.Location = new Vector3(
                this.Location.X,
                this.Location.Y + GetCurrentMoveSpeed() * deltaTime,
                this.Location.Z);
        }

        public void TranslateDown(float deltaTime)
        {
            this.Location = new Vector3(
                this.Location.X,
                this.Location.Y - GetCurrentMoveSpeed() * deltaTime,
                this.Location.Z);
        }

        public void LookAt(Vector3 location, float pitch, float yaw, float roll)
        {
            this.Location = location;
            this.Pitch = pitch;
            this.Yaw = yaw;
            this.Roll = roll;
        }

        public void SetAspectRatio(int width, int height)
        {
            this.ViewportWidth = width;
            this.ViewportHeight = height;
            this.AspectRatio = width / (float)height;
        }
    }
}

我已经知道我需要比较某种形式的三角形的法线。我只是不知道该怎么办。如果有人知道答案,我将不胜感激。如果需要任何其他代码,请告诉我。谢谢!

1 个答案:

答案 0 :(得分:2)

您可以使用currentCameraLocation - previousCameraLocation并使用简单的点积来计算相机的速度,从而确定是否碰到了三角形的背面:

var cameraVelocity = currentCameraLocation - previousCameraLocation;
bool hitBackFace = Vector3.Dot(cameraVelocity, triangleNormal) > 0;

您可能希望使用叉积计算三角形法线:

var triangleNormal = Vector3.Cross(finalVertexLocation2 - finalVertexLocation1, 
                                   finalVertexLocation3 - finalVertexLocation1);

这样做的原因是顶点法线与三角形法线不一定相同。如果在phong阴影球体上想到一个三角形,则该三角形的3个顶点中的每个顶点将具有不同的法线向量,并且它们都不与三角形法线相同。

相关问题