Not sure if this is the right forum for this question.
I need help with some linear algebra. Imagine an object (bullet etc) flying in 3D space. When the object collides with a surface it should continue to travel parallel to the surface, rince and repeat, until its travel length has been reached or it makes a “head on” collision, i.e., the direction of the object is the same as the negative normal of the surface (not likely to happen). The direction of the object along the surface must be related to the direction the object had when it reached the surface, and this is where I need help.
I am (I think) able to do the math in 2D. Below is a code example for 2D to illustrate what it is that I am looking to accomplish in 3D.
var hit = Physics2D.Raycast(rayOrigin, Vector2.right, rayLength, CollisionMask);
if (hit)
{
// Get the rotation angle.
var rotationAngle = Vector2.Angle(hit.normal, Vector2.up);
if (rotationAngle > 90.0f)
{
rotationAngle -= 180.0f;
}
// Get the new direction and length.
var direction = Quaternion.Euler(0.0f, 0.0f, rotationAngle) * Vector2.right;
length -= hit.distance;
}
Can someone help me figure out how to accomplish the same in 3D space?
Side question: Can the direction calculation above be simplified?
I must have been unclear. I want a vector that is parallel to the surface. Will change the wording in of “travel along the surface” to “travel parallel to the surface” in OP.
This is not Linear Algebra it is Trigonometry with a little sprinkle of Calculus 3 or Vector Math.
I attached a Unity Package so you can take a look.
The code
Code
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class Raytrace : MonoBehaviour {
[SerializeField]
float m_Distance = 10.0F;
void Update() {
Ray ray = new Ray(this.transform.position, this.transform.forward);
RaycastHit hit;
// Raytrace is RED
Debug.DrawLine(ray.origin, ray.origin + ray.direction * this.m_Distance, Color.red);
// Are we hitting a surface?
if(Physics.Raycast(ray, out hit, this.m_Distance)) {
// Get the angle between the direction of our raytrace and the normal.
// Since the Normal is always _|_ to the surface we have to
// Find the angle between the normal
// and the direction of our ray coming FROM the Noraml
// hence why we have to make it negative so it goes
// the other direction
float angle = Vector3.Angle(-ray.direction, hit.normal);
// Get the height from the point of contact to the height of the our
// Raytrace origin.
// The reason why we need this is so we can calculate the point
// where or Normal extended to some height is level with our point or origin
// meaning the 3D point in space where the Ray begins.
float height = hit.distance * Mathf.Sin(Mathf.PI / 2.0F - angle * Mathf.Deg2Rad);
// We now have a 3D Vector that is level with our origin.
// This allows us to create in our head a Triangle.
// If you take a look at the Scene View Window
// You will notice I made everything resemble a triangle to
// make the calculations easier.
Vector3 normalEndPoint = hit.point + hit.normal * height;
// Normal is BLUE
Debug.DrawLine(hit.point, normalEndPoint, Color.blue);
// Calculate the surface vector. This is going to be a unit vector
// so, you can then do in your calculations transform.forward = surfaceVector
// so you can 'ride' along the surface.
Vector3 surfaceUnitVector = (normalEndPoint - ray.origin).normalized;
// Surface Vector is GREEN
Debug.DrawRay(hit.point, surfaceUnitVector, Color.green);
}
}
}
I got my introduction to vector math in my linear algebra classes, and wikipedia seems to agree with me, “Vector spaces are the subject of linear algebra …”. Was a long time ago, and I am no math guru (hence this post) so you are probably right.
I just had a look at the package you included. Seems to be exactly what I was looking for (I just hope that I have defined the problem correctly). And the code and math is so clean! Now I begin to worry that my own math for the 2D solution is very suboptimal…
Thanks a ton for the code and package. It is very highly appreciated. You might have saved my day and in a very sexy way.
That’s odd you got introduced to Vector math in linear algebra as it is part of Calculus series…how did you ever do integral calculus without an understanding of Vector Calculus? From what I remember of Linear we did Vectors for a few weeks and went into abstract vector fields and matrices.
Either way its all math. Glad I could help, the way I went about to tackle this problem was I opened up my Trig book and got the equation for the AAS since you know the distance of 1 side (the ray trace from the RaycastHit.distance value) and if you solve for 1 angle you can find the other as the other would be 90-Theta. Hence why my calculation has 90.0F-angle. And the Calculus part was testing the Vectors using the angle between 2 vectors cos(theta) = A DOT B / ||A||*||B|| etc. It was a cool little exercise.
A quick test verifies that it seems to be the same (with the potential to be a tiny bit more accurate due to less floating operations), but is it always true? Drawing the triangle on paper is telling me it should be correct, but would love if someone could confirm it.
Looking at my triangle the Cosine would be the distance from the origin point to normal end point in 2D the problem is knowing that distance does not help you as you do not know the position of the normal end point. I.E. which direction does the distance travel along in 3D space? I find the height first then find the end point then create a vector from the origin to that Normal End point (the blue line) and the get the unity vector from that. The distance from the origin to the normal end point is not required as you don’t care for the purposes of your needs all you want is the direction to face.
I believe you missed that I was using different angles for the Sin and the Cos functions?
The Sin solution use “Mathf.PI / 2.0F - angle * Mathf.Deg2Rad”
The Cos solution use “angle * Mathf.Deg2Rad”
Since we are working with a right angle we have the following:
A = 90 degrees
B = angle degrees (the angle we calculated between the normal and the reversed direction vector)
C = 180 - A - B = 90 - B
a = hit.distance
c = length we are looking for
It then follows that
c = a * Mathf.Sin(C * Mathf.Deg2Rad) = a * Mathf.Cos(B * Mathf.Deg2Rad)
Dividing both sides with “a” and we are left with
Mathf.Sin(C * Mathf.Deg2Rad) = Mathf.Cos(B * Mathf.Deg2Rad)
What…? Vector is part of linear algebra and you can do calculus without using vectors; calculus is the study of change using differentials and integrals, it does not have to involve vectors. But you also have both fields combined, and that’s Vector Calculus.
Linear Algebra is the teaching of Vector Spaces, Sub Spaces etc…yes you do do Vector math somewhat but you learn that in Calculus and Trigonometry when you learn about the concept of ‘direction’. Physics requires you to have taken Calculus for this very reason NOT Linear Algebra they are not synonymous to each other as they are very different fields of study. One is more abstract. I cannot recall a single time I did any computations in Linear Algebra all I ever* did was prove a subset was part of an other set. We used vectors to illustrate a graphical representation (sometimes if possible) but that was it.
I maintain: Vector is part of Linear Algebra, not Calculus.
The order or how the concepts are introduced does not matter, Linear Algebra (study of vectors and matrices) and Calculus (study of change) are different fields.
Of course, you can have overlaps and mix of different fields for practical reasons or educational purposes.