평면 ( 3개의 점 )과 직선 (원점과 방향)의 교차점을 구하는 방법이다.

수학적 개념보다 코드가 더 이해가 빠른거 같다.


Unity 상에서 C#코드를 통해 계산했습니다. (Intersect Triangle)


무한 평면 기준

    private bool IntersectTriangle(Vector3 RayOrigin, Vector3 RayDirection, Vector3 V0, Vector3 V1, Vector3 V2)
{
Vector3 edge1 = V1 - V0;
Vector3 edge2 = V2 - V0;

Vector3 pvec = Vector3.Cross(RayDirection, edge2);

dot = Vector3.Dot(edge1, pvec);

Vector3 tvec;
if (dot > 0) tvec = RayOrigin - V0;
else
{
tvec = V0 - RayOrigin;
dot = -dot;
}

if (dot < 0.0001f)
return false;

u = Vector3.Dot(tvec, pvec);
// if (u < 0.0f || u > dot)
// return false;

Vector3 qvec = Vector3.Cross(tvec, edge1);

v = Vector3.Dot(RayDirection, qvec);
// if (v < 0.0f || u + v > dot) return false;

t = Vector3.Dot(edge2, qvec);
float flnvDet = 1.0f / dot;

t *= flnvDet;
u *= flnvDet;
v *= flnvDet;

return true;
}

코드 자체의 원리는

1. 3개의 점을 통해서 2개의 직선을 구한다.

2. 2개의 직선을 외적해서 법선 벡터를 구한다.

3. 내적을 통해 직선의 방향을 결정한다.


3 - 1 유한 평면의 경우 내적을 통해 u를 계산, 평면의 좌, 우를 넘어갔는지 판단한다.

3 - 2 유한 평면의 경우 내적을 통해 v를 계산, 평면의 위, 아래를 넘어갔는지 판단한다.


if (IntersectTriangle(Line.position, Line.forward, P0.position, P1.position, P2.position))
{
Vector3 point = Line.position + (Line.forward * t);
Gizmos.DrawSphere(point, 0.1f);
}

true 일때만 교차점이 생기고 , 교차점은 직선의 원점 + 직선의 방향 * t로 구하면 된다.


유한 평면 기준

IntersectTriangle의 주석처리된 부분을 풀어주면 유한 평면에서만 충돌하는지 검사 가능



Vector3 Direction to Quaternion (Rotation)

방향 벡터를 이용해서 쿼터니언 (로테이션) 구하기


this.front.subVectors( this.finalTarget, this.resultPos );
this.front.normalize();

현재 좌표에서 바라볼 대상의 좌표를 뺀다.

정규화 (Normalize) 하면 대상을 바라보는 Look Vector (front)가 생긴다.


this.up.set( 0, 1, 0);

Up vector를 설정한다. (일반적으로 0, 1, 0인 상황)

만약 지구와 같은 구체주변을 맴돈다면


this.up.copy( this.object.position );
this.up.normalize();


자신의 좌표를 이용해서 Up벡터를 구한다. (지구의 중점이 0, 0, 0 이라는 기준으로)


this.right.crossVectors( this.up, this.front );
this.right.normalize();

up벡터와 front벡터를 Cross 시켜서 Right 벡터를 구한다.


this.up.crossVectors( this.front, this.right );
this.up.normalize();

front와 right를 이용해서 다시 Up벡터를 구한다.


일반적으론 이렇게 나온 right, up, front에 적용시키면 로테이션이 적용된다.

이번은 Matrix를 이용해서 Quaternion을 구해보자.


Matrix mBasis = new Matrix(vRight.X, vRight.Y, vRight.Z, 0.0f,
vUp.X, vUp.Y, vUp.Z, 0.0f,
vDirection.X, vDirection.Y, vDirection.Z, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);

Quaternion qrot = new Quaternion();
qrot.W = (float)Math.Sqrt(1.0f + mBasis.M11 + mBasis.M22 + mBasis.M33) / 2.0f;
double dfWScale = qrot.W * 4.0;
qrot.X = (float)((mBasis.M32 - mBasis.M23) / dfWScale);
qrot.Y = (float)((mBasis.M13 - mBasis.M31) / dfWScale);
qrot.Z = (float)((mBasis.M21 - mBasis.M12) / dfWScale);

이렇게 나온 쿼터니언 (Quaternion)을 이용해서 Rotation을 적용시키면 된다.

acos(x) 로 구했다가 난감한 경험이 있었다.


제대로된 Angle을 구하고 싶다면


let angle = Math.atan2( y, x ) * RAD2DEG; 하면 정상적인 각도가 나옴


디스플레이 화면 특성상 Y값이 밑으로 갈수록 증가하기 때문에


실제로는 다음과 같은 식으로 구현했다.


var RAD2DEG = 180 / Math.PI;
let angle = Math.atan2( -y, x ) * RAD2DEG;


색상코드를 보면 가끔 RGB도 HEX도 아닌 Decimal로 되어있는 색상 코드가 있다.


예시

RGB : 255, 255, 255 => 16777215

RGB : 255, 0, 0 => 16711680


이러한 Decimal 색상 코드를 RGB 로 변환해 보자.


Color = 16777215


R = Math.floor( Color / ( 256 * 256 ) )

G = Math.floor( Color / 256 ) % 256;

B = Color % 256;


빛이 벽을 통해서 반사 되거나


공튕기기 게임에서 벽에 부딪힐때 반사각을 구해보자


1. 진행방향 벡터 (p) 와 충돌한 부분의 법선벡터 (n) 가 필요하다.

let n = this.result.normal; // 법선 벡터
let p = []; // 원래 벡터
p2.vec2.sub( p, this.rayClosest.from, this.rayClosest.to );

2. 진행방향 벡터의 역벡터 (-p) 를 구한다.

let minusP = [];         // 역 벡터
p2.vec2.sub( minusP, this.rayClosest.to, this.rayClosest.from);

3. 역벡터 (-p) 와 법선 벡터의 (n) 내적 (dot)을 구한다.

// 내적 계산
let dot = p2.vec2.dot( minusP, n );

4. 법선 벡터를 2배로 (2 * n) 늘린다.

// 법선 벡터 2배
let mulNormal = [];
p2.vec2.scale( mulNormal, this.result.normal, 2 );

5. 2배 길이의 법선벡터를 내적이 계산된 값만큼 스케일을 조절한다. ( 2 * n * dot(-p, n) )

let finalNormal = [];
p2.vec2.scale( finalNormal, mulNormal, dot );

6. 원래 진행방향 벡터 (p)에 5번에서 계산된 벡터 (스케일 조절된 법선 벡터) 를 더한다. ( p + 5번 결과 벡터 )

p2.vec2.add( reflect, p, finalNormal ); // 반사벡터 산출

7. 구해진 반사벡터를 정규화시킨다. ( normalize reflect )

p2.vec2.normalize( reflect, reflect );  // 정규화

8. 2d Screen의 특성에 따라 (y값이 밑으로 갈수록 증가) 역벡터를 구하면 완성( negate reflect )

p2.vec2.negate( reflect, reflect );     // 스크린 특성


마지막으로 계산된 reflect를 물체의 방향 (direction) 으로 사용하면 잘 반사된다.


- 참고 하면 좋은 수학적 개념 -

http://toymaker.tistory.com/entry/%EB%B0%98%EC%82%AC-%EB%B2%A1%ED%84%B0-Reflection-Vector

https://www.slideshare.net/cancan21st/ss-17299826

http://ifyouwanna.tistory.com/entry/%EB%B0%98%EC%82%AC%EB%B2%A1%ED%84%B0

정방행렬 == 정사각행렬 == 가로줄 개수와 (행) 세로줄 개수 (열)이 같은 행렬


항등행렬 : Identity

A행렬 X 항등행렬 = A행렬이 나오게 하는 행렬

항등행렬은 정방행렬일때만 가능하다.

전치행렬 : Transpose

원래 행렬에서 행과 열을 서로 바꿔주는것

행렬식 : Determinant

행렬식의 값이 0이라면 역행렬이 나올수 없고 행렬식의 값이 !0(0이아니라면)이면 유일한 해를 가진다.

라플라스 전개를 통한 정의 (재귀함수)를 이용해 구할 수 있다.


소행렬식 : Minor

각 차수를 제외하고 나머지를 행렬로 만든것

   1 2 3           a11의 소행렬식은             a22의 소행렬식은

   4 5 6   ->             5 6              ->              1 3

   7 8 9                   8 9                               7 9


여인자 : Cofactor

거기에 1 또는 -1을 값으로 하는 계수를 곱한것.





여기서 det == determinent , C == Cofactor

수반행렬 : adjoint

그 행렬의 전치행렬에서 각 원소의 복소켤레를 취한것

->모든 항의 Cofactor(여인자)를 구하고 행렬을 Transpose(전치) 한 행렬



역행렬 : Inverse

A행렬 X A역행렬 = 항등행렬

lAl는 A의 Determinant값 , C는 Cofactor , 차수위치에 있는 T는 Transpose



벡터 : 방향과 크기를 가지고 있다.

스칼라 : 힘의 크기 또는 위치만을 가지고 있다.


우리가 지금까지 알고있는 개념은 전부 스칼라.

총알이 날라가고 있다. 이 총알의 좌표가 아니라 총알이 어느 방향으로 얼마만큼의 힘으로 가는지 표현해야한다.

이때 사용되는게 벡터의 개념 (어느 방향으로 얼만큼의 힘으로 가는지)


+ Recent posts