1. 벡터
(1) 벡터란 차원을 행렬로 만든것이데 1x4가 행렬이라는 것을 기억해야 합니다.
(2) 보통 3차원 수학에서는 4차원으로 만들어서 w값 까지 생각해서 사용했다. W값에 대해서 잊지 말라고 4차원으로 한것이다. 대부분 게임엔진이나 개념을 설명할때는 보통 3차원으로 씁니다.
(3) 벡터들은 연산자 만으로 큰 의미를 가집니다.
스칼라라는 단어가 굉장히 많이 나옵니다.
Ex) 결과값이 스칼라 인가요? 벡터인가요?
스칼라 => float
크기를 말한다.
2. 벡터의 덧셈
- 벡터의 덧샘 결과값 => 스칼라? 벡터? -> 벡터
- 컨텐츠 어디에서 썼나요? => 몬스터 이동 xxx 이동이죠.
-> 두개의 벡터가 더해져서 새로운 방향과 위치가 나오게 되죠.
- W는 를 더하거나 빼지는 않는다.
3. 벡터의 뺄셈
- 벡터의 뺄셈 결과값 => 스칼라? 벡터? 벡터
- 컨텐츠 어디에서 썼나요? => x 오브젝트가 x오브젝트를 향하는 방향벡터를 구하려고 하죠.
A – B B – A 결과가 다를수도 있죠.
빼기로 방향을 구하고 더하기로 그 방향으로 이동시키는게 일반적인 몬스터의 행동이죠?
순서
값을 계산 후 (0,0) 좌표에 적용
4. 벡터의 곱
- 벡터의 스칼라 곱 결과값 => 벡터
- 컨텐츠에서 => 속력증가 벡터가 크기가 증가하면 기하하적으로 위치증가.
- 방향은 유지하면서 크기만 바뀐다고 합니다.
Xyz * xyz => 벡터곱
Xyz * s => 스칼라곱
5. 벡터의 정규화 (노말라이즈)
(1) 단위 벡터(unit vector)란 방향을 가지는 크기가 1인 벡터입니다. 그리고 어떠한 벡터를 단위 벡터로 만드는 것을 정규화(Normalization)라고 합니다.
(2) 노말라이즈
- 정규화 : 해당 벡터의 방향은 유지한채 크기가 1인 단위 벡터로 만드는 과정
(3) 단위벡터를 사용하는 이유
- 두개 이상의 벡터를 연산할 때 좀더 쉽고 단순하게 다룰수 있기 때문
-> 크기를 1로 고정하면 해당 벡터의 방향만을 고려하면 되기 때문에 그 다음의 연산을 단순하게 해주는 효과
ex) 몬스터가 플레이어 방향을 알고 추격할때 사용
6. 벡터의 정규화 2 (노말라이즈)
(1) 벡터(Vector) : 벡터는 크기와 방향을 가진 양을 나타내는 수학적 개념
- 벡터는 크기와 방향을 가지는 양으로 크기는 벡터의 길이를 의미하며, 방향은 벡터가 가리키는 방향을 의미합니다.
(2) 스칼라(Scalar) : 크기만을 가지고 방향이 없는 양을 의미 (엔진에서는 float 값을 나타냄)
-> 벡터와 반대되는 개념으로 단순히 크기만을 가지고 있습니다.
(3) 벡터의 정규화 => nomralize => 결과값은? 벡터
언제쓰나요? => 미친듯이 많은 상황에서 사용합니다.
벡터의 길이 n일때 이 n을 1로 만드는것.
보통 다른 값을 추론해내야할때 많이 사용된다.
(4) 가장 많이 사용되는 경우 추론
-> A 공식 B = C
-> A * B = C
-> B를 노말라이즈 하면
-> A * 1 = C
- ABC 중 나는 지금 2가지 값을 알고 있고 2가지 값중 하나를 노말라이즈 했다고 하면 그 값은 1이 되기 때문에 나머지 하나의 값을 쉽게 알아내는데 많이 사용됩니다.
- 노말라이즈 하는 방법은 => 피타고라스의 정리랑 연계됩니다.
- 보통 벡터의 수치 그자체는 삼각형을 만들수가 있고 밑변 높이 빗변으로 이루어져 있게 됩니다.
피타고라스가 말하기를
밑변제곱 + 높이제곱 = 빗변제곱
A제곱 + B제곱 = C제곱
(5) 벡터의 길이란?
- 즉 벡터의 길이가 1이라는 것은 빗변의 길이가 1이라는것을 의미하는거죠.
- 길이가 n인 벡터가 있을대 이 벡터는 xy로 이루어져 있을것이다.
- Xy에다가 빗변의 길이를 나누면 삼각형은 자연스럽게 빗변의 길이가 1인 삼각형으로 바뀐다는 것이다.
이게 노말라이즈 입니다.
- 노말라이즈를 하려면 빗변의 길이를 알아내야 하죠
- 빗변의 길이는
밑변 * 밑변 + 높이 * 높이 = 빗변제곱
Std::sqrt(밑변제곱 + 높이제곱) = 빗변의 길이
제곱을 풀어주는 함수를 사용해서 구한다.
- 방향벡터를 구하려면 뺄샘을 합니다.
-> 뺄셈으로 나오는 결과는 순간이동 결과라고 표현했습니다.
-> 뺄셈을 통해서 나온 방향과 크기를 가진 값을 노말라이즈는 방향만 남긴다는 표현을 많이 사용한다.
- 하지만 정확하게 말하면 크기가 1이기 때문에
-> 길이가 1인 벡터를 단위 기준이 된다고 해서 단위 벡터라고 부르기도 합니다.
- 노말라이즈 사용처
ex)
몬스터가 플레이어를 향하는 방향을 뺄셈으로 구하고 그방향으로 자신의 스피드대로 움직이려고 할때
방향을 노말라이즈 해야 스피드가 정확하게 적용되게 된다. 그때 노말라이즈 한다.
7. 벡터의 내적
- dot dot 프로덕트
static float DotProduct3D(float4 _Left, float4 _Right)
{
float Result = (_Left.X * _Right.X) + (_Left.Y * _Right.Y) + (_Left.Z * _Right.Z);
return Result;
}
(1) 내적의 결과 => 스칼라
ex)
사용처가 미친듯이 많이 나오는데. 노말라이즈랑 똑같은데 가장 많이 등장하는 것이
가장 많이 나오는 것은 각도구하기 입니다.
(2) 공식
- a·b = ax*bx + ay*by + az*bz
-> 이게 내적 기본인데. 수학자들이 이게 같다는 것을 밝혀냈다.
a·b = ax*bx + ay*by + az*bz == a의 빗변의 길이 b의 빗변의 길이 * Cos(세타);
-> 간혹가다가 벡터에 |A| A벡터의 빗변의 길이를 의미한다
-> a·b = ax*bx + ay*by + az*bz == a의 빗변의 길이 b의 빗변의 길이 * Cos(세타);
- 위의 공식을 보고
A와 B벡터가 있는데.
A벡터를 노말라이즈 하고 B벡터를 노말라이즈 했다.
-> 1 * 1 * cos(세타)
-> 각도는 세타
-> 노말라이즈한 2개의 벡터를 내적하면 남는결과는 무조건 cos(세타)만 남게 됩니다.
-> Std::acos(노말라이즈한 벡터의 결과물) => A와 B 벡터의 라디안 각도가 된다.
- 벡터는 1개일때가 많다.
->보통 1개의 벡터만 있을때가 많다.
-> 어떤 벡터가 있던지 1,0 벡터랑 내적하면 된다. 이때 보통 2차원이면 주의해야할게 있다.
0~180 0~3.14까지 밖에 안나오므로 y값을 기준으로 위아래로해서 각도를 더해서 360도를 표현한다.
사용 예시 -> 시야각?
(1) 시야각 (View 행렬)
(2) 그림자
(3) 회전하는 사각형 충돌 (OBB)
(4) 길이가 1인 n각도의 회전한 벡터의 X값으로도 사용
- 길이가 1인 n각도의
- 내적을 하면 정사영을 구할 수 있다.
8. 벡터의 외적
(1) 외적은 벡터와 벡터로 이루어지며 결과는 다시 벡터가 나옵니다.
(2) Cross라고 불립니다.
(3) View 행렬 -> 회전 행렬 방향 구할 때 쓰임
- 카메라가 회전의 반대방향으로 회전시켜야 합니다.
-> 회전행렬을 만들어 내야 합니다.
- 회전행렬을 구할수 있는 방법
-> 코시시코를 통해서 구하는 방법
-> 수직인 3개의 벡터의 모임이 곧 => 회전행렬
-> 회전행렬이 되려면 -> 3개의 벡터가 무조건 수직이어야 해.
- A와 B벡터와 수직인 C벡터를 구한다. -> Z와 Y의 외적 공식을 통해 X의 값을 알 수 있다.. (외적)
- 내적과 같이 B와 A를 바꾸면 방향이 반대가 될 수 있다.
(4) 첫번째 어느방향으로 돌아야 할지를 알수 있습니다.
(5) 이특성을 이용해서 캐릭터의 회전방향 매쉬의 앞면과 뒷면도 알아낼수 있다.
9. 행렬
(1) 3차원 변환행렬 1x4벡터를 곱해서 변화시키는 행렬을 3차원 변환행렬 ->3차원 변환행렬을 4x4로 이루어집니다.
(2)
크 * 자 * 이 * 공 * 부
기 전 동 전 무
행 행 행 행 행
렬 렬 렬 렬 렬
struct
{
float v00;
float v01;
float v02;
float v03;
float v10;
float v11;
float v12;
float v13;
float v20;
float v21;
float v22;
float v23;
float v30;
float v31;
float v32;
float v33;
};
float4 ArrVector[4];
float Arr1D[16] = { };
float Arr2D[4][4];
10. 항등 행렬
(1) 항등행렬이라는 녀석이 있는데 유일하게 결과가 교환법칙이 성립하게 만드는 행렬이 존재
(1) 행렬은 무조건 기본적으로 항등행렬로 만들어 놓는것을 원칙
1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 1 |
(3) 행렬 == 차원 공간이라고 합니다.
- 카메라가 바라보는 공간
- 내가 바라보는 공간
- 캐릭터의 공간
-공간을 정의하는 것 크기 회전 위치 이미 항등행렬에도 회전과 위치와 크기가 다 들어가 있는 상태입니다.
void Identity()
{
// 이함수 기억해 놓으세요.
// Arr1D 주소값위치부터 100
// sizeof(float) * 16 크기만큼을 164 번지 까지를
// 0으로 채워라.
memset(Arr1D, 0, sizeof(float) * 16);
Arr2D[0][0] = 1.0f;
Arr2D[1][1] = 1.0f;
Arr2D[2][2] = 1.0f;
Arr2D[3][3] = 1.0f;
}
11. 크기 행렬
(1) 어떤 크기를 가진 벡터를 곱해도 그대로 나옴
// 크기 행렬이라고 합니다.
void Scale(float4 _Value)
{
Identity();
Arr2D[0][0] = _Value.X;
Arr2D[1][1] = _Value.Y;
Arr2D[2][2] = _Value.Z;
Arr2D[3][3] = 1.0f;
}
12. 위치 행렬
void Position(float4 _Value)
{
Identity();
Arr2D[3][0] = _Value.X;
Arr2D[3][1] = _Value.Y;
Arr2D[3][2] = _Value.Z;
}
13. 회전 행렬
(1) 회전행렬은 실제 우리눈에 직접적으로 보이는 면적과 연관이 있기 때문에 크기와 공간을 공유 합니다.
(2) 그리고 그렇기 때문에 더더욱 순서 크기를 먼저 변환하고 회전을 시켜줘야 합니다.
(3) 회전행렬은 3개의 벡터로 이루어져있다. X축 y축 z축 3개의 벡터는 수직을 이루어야 한다.
- Z 기준
void RotationZRad(float _Angle)
{
// 코시시코
Identity();
Arr2D[0][0] = cosf(_Angle);
Arr2D[0][1] = sinf(_Angle);
Arr2D[1][0] = -sinf(_Angle);
Arr2D[1][1] = cosf(_Angle);
}
- Y축 기준
void RotationYRad(float _Angle)
{
Identity();
Arr2D[0][0] = cosf(_Angle);
Arr2D[0][2] = -sinf(_Angle);
Arr2D[2][0] = sinf(_Angle);
Arr2D[2][2] = cosf(_Angle);
}
- X축 기준
void RotationXRad(float _Angle)
{
Identity();
Arr2D[1][1] = cosf(_Angle);
Arr2D[1][2] = sinf(_Angle);
Arr2D[2][1] = -sinf(_Angle);
Arr2D[2][2] = cosf(_Angle);
}
14. 월드 행렬 ((Mesh(점))로컬 스페이스 * 월드 행렬 => 월드 스페이스)
(1) 크기(4X4) * 회전(4X4) * 이동(4X4) -> 최종 크자이 행렬 완성
- 크기
- 자전
- 위치
15. View 행렬 : 월드 스페이스 -> 뷰 스페이
(1) 카메라 행렬을 의미
(2) 뷰행렬의 역할 -> 어떤 카메라가 어떤 물체들을 보고 있다를 만들어 내는 겁니다.
(3) 내 눈에 보이는 물체들을 정렬해주는 역할을 합니다.
(4)뷰행렬을 구하기 위해서는 내적과 외적을 알아야 합니다.
(5) 카메라가 기준되어
카메라의 위치가 0,0,0
카메라의 회전이 0,0,0 이 되는 행렬입니다
(6) 이 이야기는 반대로 말하자면
- 카메라를 제외한 모두가 카메라를 기준으로 공전하는 행렬이 되는것입니다.
(7) 뷰행렬의 목적
- 투영행렬 하기전에 모든 점들을 카메라 앞에 존재하는 기준으로 만들려고 하는것. 눈앞에 보이는 점들을 가려내기 위해서
- 카메라는 45도 돌아가 있었다
- 하지만 뷰행렬의 역할은 카메라를 회전하지 않은 상태로 만드는게 첫번재입니다.
- 그러려면
-> 당연히 카메라가 45도 돌아가 있었다면 나머지 모두는 -45도 돌아가게 만들어야 한다는것.
- const float4 _EyePos 바라보는 사람의 위치
- const float4 _EyeDir 바라보는 사람의 바라보는 방향
- const float4 _EyeUp 바라보는 사람의 바라보는 방향과 수직인 벡터.
// const float4 _EyePos 바라보는 사람의 위치
// const float4 _EyeDir 바라보는 사람의 바라보는 방향
// const float4 _EyeUp 바라보는 사람의 바라보는 방향과 수직인 벡터.
float4x4 View(const float4 _EyePos, const float4 _EyeDir, const float4 _EyeUp)
{
// 카메라가 회전의 반대방향으로 회전시켜야 합니다.
// 회전행렬을 만들어 내야 합니다.
float4x4 View;
// 회전행렬을 구할수 있는 방법은
// 코시시코를 통해서 구하는 방법이 있다.
// 회전의 정의
// 수직인 3개의 벡터의 모임이 곧 => 회전행렬
// 회전행렬이 되려면
// 3개의 벡터가 무조건 수직이어야 해.
// 크기요소의 개입을 막기 위해서
FVector Up = _EyeUp.Normalize3DReturn();
FVector Forward = _EyeDir.Normalize3DReturn();
FVector Right = FVector::Cross3D(Up, Forward);
Up.W = 0.0f;
Forward.W = 0.0f;
Right.W = 0.0f;
// 외적해서 나머지 부분을 구하고
View.ArrVector[0] = Right;
View.ArrVector[1] = Up;
View.ArrVector[2] = Forward;
View.Transpose();
float4 NegEyePos = -_EyePos;
NegEyePos.W = 1.0f;
// 결국 현재 카메라의 위치를 회전시켜서 빼줘야 한다.
// float4 Result = NegEyePos * View;
// 이때 내적이 사용됩니다.
View.ArrVector[3].X = float4::DotProduct3D(Right, NegEyePos);
View.ArrVector[3].Y = float4::DotProduct3D(Up, NegEyePos);
View.ArrVector[3].Z = float4::DotProduct3D(Forward, NegEyePos);
*this = View;
return View;
}
16. 전치 행렬
(1) 어떤 회전행렬이 존재할때 그와 완전히 역방향의 회전행렬을 구해내는 것은 굉장히 쉽다.
전치행렬이라는것을 이용하면 된다.
(2) 이렇게 가운데 축을 기준으로 회전시킨 행렬을 전치 행렬이라고 하는데.
(3) 이에 대한 결과는 회전행렬에는 이미 정해져있다.
-> 기존 회전행렬에 완전히 반대되는 회전행렬이 만들어 집니다.
void Transpose()
{
float4x4 Result = *this;
for (size_t y = 0; y < 4; y++)
{
for (size_t x = 0; x < 4; x++)
{
Result.Arr2D[y][x] = Arr2D[x][y];
}
}
*this = Result;
}
// 전치행렬
float4x4 TransposeReturn()
{
float4x4 Result = *this;
for (size_t y = 0; y < 4; y++)
{
for (size_t x = 0; x < 4; x++)
{
Result.Arr2D[y][x] = Arr2D[x][y];
}
}
return Result;
}
17. 투영 행렬
- 투영행렬의 목적은 모든 점의 X를 화면 -1~1 / 너비 기준으로 한 값으로 바꾸려고 하는 것 당연히 화면 바깥에 나가는 애들도 있고 걸치는 애들도 있고 하지만 기준이 생겼다,
- -1~1 바깥으로 나간 애들은 전부다 화면 바깥에 있는 점들이다.
X -1~1
Y -1~1
Z 0~1의 범위로 압축한다가 기본입니다.
(1) 원근 투영
- 원근감을 표현하기 위한 원근 투영행렬방식과 Real3D게임에 사용된다.
- 원근 투영은 3각형으로 이루어져 있는데 Near는 0일수가 없다
void PerspectiveFovRad(float _FovAngle, float _Width, float _Height, float _Near, float _Far)
{
Identity();
// w에 z값 이전을 위한 값
Arr2D[2][3] = 1.0f;
// 그 w값에 1안더해 지게 만들려고
Arr2D[3][3] = 0.0f;
// _FovAngle y각도
float Ratio = _Width / _Height;
float DivFov = _FovAngle / 2.0f;
// x쪽 값이 된다.
Arr2D[0][0] = 1.0f / (tanf(DivFov) * Ratio);
Arr2D[1][1] = 1.0f / tanf(DivFov);
Arr2D[2][2] = _Far / (_Far - _Near);
Arr2D[3][2] = -(_Near * _Far) / (_Far - _Near);
}
(2) 직교 투영행렬
- 2D게임에 많이 사용됨
void OrthographicLH(float _Width, float _Height, float _Near, float _Far)
{
// _Near 가장 가까이 보이는 평면
// _Far 가장 멀리까지 보이는 평면
Identity();
// N F
// 100 1000
// Range 900
float fRange = 1.0f / (_Far - _Near);
Arr2D[0][0] = 2.0f / _Width;
Arr2D[1][1] = 2.0f / _Height;
Arr2D[2][2] = fRange;
// 각벡터에 Z에 영향을 미치는 부위
Arr2D[3][2] = -fRange * _Near;
}
18. 뷰포트 행렬
- 출력할 윈도우(window)와 윈도우 내에서의 출력 공간(viewport)가 필요하다.
-> 출력 공간(viewport)를 설정하여 실질적으로 출력되는 공간을 설정해줄 수 있다.
// 모니터 크기로 확장
void ViewPort(float _Width, float _Height, float _Left, float _Right, float _ZMin, float _ZMax)
{
Identity();
Arr2D[0][0] = _Width * 0.5f;
Arr2D[1][1] = -_Height * 0.5f;
Arr2D[2][2] = _ZMax != 0.0f ? 1.0f : _ZMin / _ZMax;
Arr2D[3][0] = Arr2D[0][0] + _Left;
Arr2D[3][1] = -Arr2D[1][1] + _Right;
Arr2D[3][2] = _ZMax != 0.0f ? 0.0f : _ZMin / _ZMax;
Arr2D[3][3] = 1.0f;
}
tip
1. W가 곧 이동을 몇% 적용받을거냐에 대한 수치
// 가장 친숙한 각도 체계는
// x 30 y 20 z 60
// -nan -nan -nan => 짐벌락.
// 수학자들이 짐벌락현상과 등등을 경험하고
// 그 회전을 대체할수 있는 안전한 회전 방식을 고안해 냈다.
// 그게 사원수.
// x 30 y 20 z 60 <= 짐벌락은 결국 발생한다.
// 행렬이 3이다.
// x 30 y 20 z 60 => float4로 변형시킨다.
'전공 정리' 카테고리의 다른 글
네트워크 지식 최종 (클라 - 서버) (0) | 2024.09.07 |
---|---|
렌더링 파이프라인 이론 -> 엔진 분석 (2) | 2024.08.28 |
개발 관련 지식 (0) | 2024.08.20 |
Unreal 최종 정리 (2) | 2024.08.19 |
C++/ CS 최종 정리 (0) | 2024.08.19 |