우리가 보고있는 곳은 게임 월드
찍고 있는 카메라가 월드의 원점에 있는 것이다.
우리가 보고있는 정면 방향이 z축 평행한 방향
물체의 상태값은 해당 물체의 트랜스폼에 저장되어있다.
확대는 먼저 확대하고 이동을 시킴
회전은 ?
회전도 로컬스페이스에서 회전을 먼저 시켜놓고 이동해야한다.
최종 월드 행렬 = 크기행렬 x 회전행렬 x 이동행렬 순서로 해야한다.
행렬 4 by 4 행렬이기 때문에
z축 회전 -> z 가 그대로니까
cosa sina 0 0
-sina cosa 0 0
0 0 1 0
0 0 0 1
x축 회전 -> x 가 그대로니까
1 0 0 0
0 cosa sina 0
0 -sina cosa 0
0 0 0 1
y축 회전 -> y 가 그대로니까
cosa 0 -sina 0
0 1 0 0
sina 0 cosa 0
0 0 0 1
y축 행렬은 50도 돌려달라고 하면 -50도 돌려준다.
다이렉트가 왼손좌표계를 쓴다..
왜 y축만 반대로 알려주냐?
회전 방향 때문이다..
반시계방향으로 회전시키고 싶기 때문이다...
혼자만 원점기준에서 바라보면 시계방향으로 돌기 때문에 반시계로 돌고 싶어서?이다..
x축을 쳐다보고 있는 입장에서는 반시계방향으로 돈다.
z축도 반시계이다.
x축회전행렬 -> XMMatrixRotationX(각도)
cull-back 컬링?
카메라 입장에서 뒷면인 것은 걸러낸다.
레스터라이저에서 정점들이 NDC 공간 안에 들어오는지 먼저 따진다.
레스터라이저에서 디폴트로 컬링모드가 있는데 cull-back이 디폴트 모드이다.
cull-back -> 뒷면을 걸러낸다는 의미이다.
내부에 들어오는 픽셀을 찾을 이유가 없으니 PS가 호출이 안되니 최종 랜더타겟이 출력되는 것도 없다.
뒷면은 기준은 뭘까?
버텍스버터 인덱스 버퍼를 이용해서 로컬스페이스에서 정면에서 배치되어있는데
레스터라이저에서 뒤집힌 상태이면 뒷면이라고 판단한다. (반시계방향으로 접근)
시계방향을 front 반시계방향을 back 이라고 한다. (다이렉트가 정함)
최종적으로는 회전된 상태로 NDC공간에 간다.
레스터라이저 입장에서는 뒤집혔으니까 반시계방향으로 접근하게 된다.
로컬스페이스에서 시계방향으로 배치해놨던게 최종적으로 월드 스페이스에서 배치될 때는 뒤집혀서 배치가 됐기 때문에
뒷면이라고 레스터라이저가 판단을 한다.
판단을 하는 방법
세 개의 정점을 이용해서 평면을 구성했을 때 평면에 수직한 노말 방향으로 튀어나온 방향이랑 우리가 바라보는 방향인 시선 백터를 내적해서 둘 사이의 각도 체크를 해서 앞면인지 뒷면인지 알 수 있다.
접근하는 순서대로 외적을 해서 점 세개의 수직한 방향을 알아내고, 그 방향과 쳐다보는 방향을 다시 내적해서 각도체크를 해서 알 수 있다.. -> 레스터라이저가 알아낸다.
외적은 수직한 방향을 얻을 때 사용
내적은 두 백터의 사잇값을 구할 때 사용
이걸 이용해서 수직방향이 어디인지에 따라 내적을 했을 때 각도가 다르게 나오기 때문에 앞면인지 뒷면인지 따질수 있다.
뒷면이라고 판단하면 안 나온다.
로컬스페이스에서 처음부터 시계방향으로 셋팅한 이유는 로컬스페이스에서 오른쪽 그림과 같이 저기를 z축이라고 두면,
정면을 시계방향을 기준으로 배치를 해두어야 회전하지 않았을 때 카메라에 그대로 보인다.
회전하지 않았을 때 정면셋팅을 하기 위해 시계방향 순서대로 인덱스를 잡아둔 것이다.
월드에서 배치될 때 회전에서 배치되면, 레스터라이저까지 넘어가게 되면 뒤집힌 채로 넘어가게 되니까 ㄱ이걸 뒷면으로 인식해서 화면에 나오지 않는다..
뒷면은 왜 안그리냐?
우리가 그리는 매쉬는 단일면, 평면이다.
3d 에서는 캐릭터 정점이 볼륨이 생긴다.
정면에서 봤을때 앞 면이고 뒷통수 안 쪽은 뒷 면이다.
앞이 뒤를 가리기 때문에 굳이 그릴 이유가 없다.. 안으로 파고들어간 면을 그릴 이유가 없다.
내가 보는 것은 앞면이기 때문이다. (쳐다보는 방향에 따라서 앞면 뒷면은 언제든지 바뀔 수 있다)
정점들의 인덱스는 쳐다봤을 때 기준으로 시계방향으로 잡아둔다.
그렇기 때문에 오른쪽 면에서 시계방향으로 그려놔도 내가 보는 방향(왼쪽에서 바라봄)에서 세 개의 정점은 반시계로 뒤집혀 보인다.
안 쪽으로 들어간 면을 내가 안에서 쳐다보는 느낌으로? 그릴 이유가 없다.. -> 최적화를 해둔 것
이런 것을 컬링하지 않았다면 안쪽으로 파고들어간 부분도 그려졌을 것이다.
하지만, 깊이판정으로 인해 앞쪽 뚜껑이 뒤쪽 뚜껑을 다시 덮어서 의미없는 픽셀쉐이더를 하는 것이다.
단면구조이기때문에 한바퀴 감싸듯이 어떤 형태가 있는게 아니다.
볼륨있는 메쉬가 아니라 그냥 단면구조이다.
갑자기 뒤로 돌았다고 해서 화면에서 없어진 것처럼 사라지면 이상하다.
컬링모드는 보통 3D 모드의 3차원 메쉬에 적용되도록 만들어져 있다.
내가 쳐다보는 방향의 반대에서 물체를 쳐다보면 무조건 반시계로 보이게 된다.
그래서 가끔 버그로 캐릭터 몸 안으로 들어가면 뻥 뚫린 상태로 겉에(배경?)만 보인다. -> 컬링이 된 경우
왜냐면 캐릭터 안으로 들어가서 어디를 쳐다봐도 다 내 화면 기준으로 반시계방향이어서 뒤집힌면으로 보이기 때문이다 .
(밖에서 쳐다봤을때 시계방향으로 되게 셋팅을 해놔서)
컬링모드는 제어할 수 있다.
레스터라이저 옵션을 만들어서 컬링을 하지 않는 방식으로 잡아줄 수가 있다.
이전엔 카메라 뷰행렬이 변화가 없게 해놨음
카메라가 월드 원점에서 z축을 바라보게 해놨기 때문이다.
월드에서 뷰로의 변화가 없게 해놨다.
카메라가 있는 곳을 항상 원점으로 생각을 하고(투영하기 위해) 카메라가 쳐다보고 있는 방향을 z축으로 삼는다.
그럼 기존에 깔아놨던 월드에 배치됐던 정점들의 정보들은 다 틀어져버린다.
월드 원점 기준으로 배치를 해놓은 것인데 갑자기 다른 곳이 원점이 되어버렸기 때문이다.
그랬을 때, 좌표들은 다 재계산이 되어 배치가 되어야한다. (기준이 바꼈기 때문에) -> 행렬을 이용한다.
월드에 있던 좌표들을 뷰공간으로 넘어가게 해주는 행렬이 상태행렬이 뷰행렬이다.
이전에는 상태행렬이 필요 없었다.
월드 스페이스 = 뷰스페이스 였다. -> 뷰 행렬이 단위행렬이었기 때문에 월드좌표=뷰좌표
카메라 본인이 어디를 쳐다보고 있는지 어디에 위치해있는지를 알아야한다.
카메라가 본인이 있는 곳을 원점으로 잡아야하기 때문이다.
카메라오브젝트가 월드 어딘가에 있을 것이고 그 위치를 그 월드상에서의 좌표니까
거길 원점으로 다시 생각을 해야한다.
카메라가 쳐다보는 방향은 z축을 그대로 유지한 채로,
월드상에서의 카메라를 다시 원점으로 오게 한 만큼 다같이 이동을 하는데
그럼 그 공간이 뷰공간이 된다.
뷰행렬은 상태행렬 규칙을 그대로 따른다.
4행파트에 이동값이 있어야 한다.
뷰행렬에 적용받을 애들은 물체이다
물체들 입장에서는 카메라의 위치를 거꾸로 음수로 빠지는 이동을 해야한다.
4행자리에 카메라본인의 좌표값을 음수로 넣어야한다.
월드좌표에 뷰행렬을 곱하는데 뷰행렬의 4행에 이동파트가 있는데
카메라 본인의 좌표가 x,y,z 이면,
x만큼 y만큼 z만큼 음수로 빠져야 원점으로 간다.
그래서 4행을 카메라 본인 좌표의 음수로 셋팅을 해두는 것이다.
이 이동행렬을 적용받는 입장에서는 카메라 좌표 반대방향으로 이동을 하게 된다.
카메라를 원점으로 끌고 오는 만큼 동일한 이동량을 물체들에게 주는 것이다.
그때의 좌표가 뷰스페이스상의 좌표이다.
// 이동행렬
Vec3 vPos = Transform()->GetRelativePos(); -> 카메라의 현재위치
m_matView =XMMatrixTranslation(-vPos.x, -vPos.y, -vPos.z) -> 카메라를 원점으로 끌고 오는 만큼 동일한 이동량
카메라를 땡겨왔는데 z축을 안 바라보고 있을 수도 있다.
이동하고나서 z축을 바라볼 수 있게 축 방향으로 회전을 해준다.
뷰행렬을 적용받는 애는 카메라 트랜스폼값 음수만큼 이동을 하고, 카메라가 보고있는 방향으로 되돌리는 만큼 회전을 시킨다. -> 이동하고 회전
내 눈앞에 들어오는 애들이 z축 앞에 있으면 된다.
// 회전행렬
단순하게 y축 회전이 아니다.
다음시간에..
// 카메라를 무조건 z축을 바라보게 해서
카메라 스크립트 -> 카메라를 움직여야하기 때문에 만듦.
틱에서는 카메라를 이동시킨다.
Vec3 vPos = Transform()->GetRelativePos(); -> 카메라의 현재위치
카메라가 오른쪽에 있으면, 카메라가 원점으로 가는 만큼 물체도 따라가서 화면의 우측에 걸리고 있는 것이다.
물체는 카메라가 원점으로 가는 만큼 왼쪽으로 빠졌기 때문에 화면상에서 왼쪽에 보이게 되는 것이다.
만약 카메라가 z축 정면이 아닌 다른 곳을 보고 있다면 ?
transform 컴포넌트에 물체의 현재 방향정보를 넣는다. (전혀 회전하지 않았을 때를 기준으로)
로테이션 값이 전부 0 일 때의 기본 방향정보는 축이랑 일치한다.
transform에 물체가 전혀 회전하지 않았을 때의 기준으로 기본 방향 정보를 정면을 프론트 백터 윗쪽을 업백터 오른쪽을 라이트 백터 정보를 넣을 것이다.
업백터 = y축
프론트백터 = z축
라이트 백터 = x축
세 백터는 왼손좌표계의 축들과 일치한다.
축 회전을 했을 때, 물체의 정점정보만 회전하는게 아니라 본인 기준으로 정면 오른쪽 위쪽은 어디인지 방향정보도 같이 실시간으로 회전이 될 것이다.
이렇게 방향정보도 실시간으로 갱신을 해놔야 코드를 짤 때 캐릭터에게 프론트 방향으로 가라고 하면 자연스럽게 현재 자기 프론트를 향해 앞으로 걸어간다..
쳐다보고 있는 방향이 항상 프론트,
위가 업
캐릭터가 회전할때 방향정보도 같이 실시간으로 매칭시켜 회전시키면,
회전되고 나서의 현재의 프론트 롸이트 업을 알 수 있게 된다.
트랜스폼헤더에 enum class DIR 으로 right, up, front 를 준다. -> 방향정보를 만듦
처음에 트랜스폼이 만들어졌을 때의 초기방향 셋팅을 기본축이랑 일치하게 셋팅한다.
finaltick 에서 회전행렬을 이용해서 현재 각각의 방향들도 똑같이 회전을 시켜야한다.
캐릭터가 회전이 돼서 배치될때 본인의 우측방향 업방향 전방방향도 같이 회전이 된다.
방향이 회전을 한다.
회전행렬은 기본적으로 4 by 4 로 구성되어 있기 때문에
회전행렬만 따로 뽑으면 이동값이 없다.
그래서 네번재 동차좌표를 신경쓸필요가 없다.
회전행렬에 회전양이 없이 때문이다.
만약에 완성된 월드행렬에 방향백터에 적용시킨다고 생각해보자..
기본 상태의 백터 (회전하지 않았을 때 우측백터는 x축)
월드행렬에는 크기, 회전, 이동이 있기 때문에 방향 좌표에는 이동이 적용이 되면 안되기 때문에 네번째를 0(동차좌표)으로 확장시켜서 월드 행렬을 곱해야한다. -> 1으로 두면 이동(4행)이 적용이 되기 때문에~
상태행렬 안에서 회전부분만 영향을 받고 싶으면 동차좌표를 0로 하고, 곱해지는 백터가 방향성 정보라는 걸 알린다.
어차피 월드행렬 구성되기 전에 회전행렬만 따로 뽑혀 있어서 이걸 곱할거니까 굳이 모든 상태가 다 누적되어 있는 월드 행렬을 곱해가지고 방향 백터를 할 바에 회전파트만 딱 만들어놓은 회전행렬이 있으니까
(3by 3 행렬)축별 행렬이 계속 곱해져서 누적돼서 구성이 되어있기 때문에 (스케일파트랑 겹쳐있음)
어차피 적용이 안되기 때문에 0 이든 1이든 상관없다.
4번째 동차좌표가 뭐냐에 따라 coord(coordinate) 와 normal 으로 나뉜다
XMVector3TransformCoord -> 4번째를 1로 둬서 이동에 영향을 받게
XMVector3TransformNormal -> 방향정보라고 판단되면 상태정보인 이동파트는 영향을 받으면 안되기 때문에 4번째를 0으로 둬서 영향을 안받게 한다.
방향을 구현하는 이유 ? -> 뷰행렬을 구현하는데 사용
이것을 이용해서 뷰의 회전행렬을 ㄱ할 수 있는지