[PhysX] 1화 Primary Shape #2

Grphics 2009. 4. 28. 21:43

이제!!! PhysX를 본격적으로 살펴보겠습니다. 좀 난이도 높아질 것입니다. 찬물 한잔 하시고... 시작합니다.


가장 먼저 SDK를 초기화 하는 방법부터 살펴보겠습니다.

라이브러리를 초기화하는 방법은 위의 한 줄이면 손쉽게 초기화가 가능합니다. 항상 리턴 값을 확인하도록 합니다.

그 다음으로는 Scene을 생성해야 합니다.


Scene을 생성하기 위해서는 Scene Descripter를 생성하고 이를 인자로 넘겨야 합니다. 여기서는 기본 중력값에 대한 인지와 물리 엔진의 계산 형태를 넘겨줍니다. NX_SIMULATION_SW는 소프트웨어적으로 계산한다는 의미입니다. NX_SIMULATION_HW인 경우, 물리 엔진 계산이 가능한 카드를 이용하여 하드웨어 가속을 받는다는 의미입니다. 일단 카드가 없으니 NX_SIMULATION_SW로 설정하고 생성합니다. PhysX에서는 대부분의 객체를 만들 때, 각각에 해당하는 Descripter가 있습니다. 그 변수에 우리가 원하는 객체의 매개변수를 입력하고 객체를 생성하면 됩니다.

다음은 라이브러리에서 사용할 전역설정을 수행합니다.

NX_SKIN_WIDTH는 물체들의 표면 깊이에 대한 설정입니다. 물체간에 충돌이 일어났을 때, 어느 깊이까지 충돌을 허용하겠냐는 의미입니다. 이번 설정에는 0.01m(1cm)까지는 Overlap을 허용하고 그 이상은 Collision이 일어나는 것입니다..

다음은 디버깅을 위한 파라미터들입니다. NX_VISUALIZATION_SCALE은 충돌 객체와 시각적 객체와의 크기 관계입니다. 1이면 1:1 크기 비율을 사용한다는 의미입니다. NX_VISUALIZE_COLLISION_SHAPES는 충돌하는 객체를 렌더링할 것인가를 의미합니다.
NX_VISUALIZE_ACTOR_AXES는 각 물체의 로컬 좌표계를 화면에 렌더링할 때, 몇개의 축을 렌더링 할 것인가를 의미합니다.



자, 이제 마지막 할 일은 우리가 생성하는 모든 물체에 공통적으로 재질(Material)을 설정하는 것입니다.. 재질(Material)은 충돌에 대한 정의, 표면 속성을 적용하는 것으로, 쉽게 말해, 어떻게 튀고, 미끌어지고, 굴러가는지를 말합니다.

위의 소스는 Scene의 모든 물체는 반발계수 0.5, 정지 마찰력 0.5, 운동마찰력 0.5의 속성을 가진다는 의미입니다. 물리에 해박한 지식은 없지만 각각 옵션을 잠깐 설명하겠습니다. 반발계수는 물체가 다른 물체와 충돌했을때 다시 튀어 나가는 정도를 나타내는 계수로, 출돌 전후의 상대속도의 비로 구할 수 있습니다. 충돌 후 두 물체의 상대 속도가 출돌 전에 비해 얼마나 작아지는지를 나타냅니다. 반발계수가 1이면 완전탄성충돌이라고 부르며, 반발계수가 0이면 완전비탄성충돌이다. 완전탄성충돌은 당구공을 생각하면 좋을듯하고, 완전비탄성충돌은 진흙구슬 두개를 굴려서 부딧혔을때를 생각하면 좋을 것 같습니다. 잠시 정지/운동마찰력을 설명하겠습니다. 이 두개의 개념은 거의 항상 같이 나오는 것으로 다음 그래프를 보면 좀더 이해하기 좋습니다.

그림 1. 정지 마찰력과 운동 마찰력의 관계

정지마찰력은 물체를 움직이기 위한 마찰력을 의미합니다. 예를 들어, 우리가 멈춘 차를 움직인다고 할 때, 처음 움직이기는 힘든데, 움직이기 시작하면 그리 큰 힘이 들지 않는 것을 경험을 통해 알고 있을 겁니다. 물체가 움직이기 시작하면 운동마찰력이 적용됩니다. 많은 경우에서 운동마찰력은 정지마찰력보다 작기 때문에 그런 경험을 할 수 있는 것입니다. 공식으로는 다음과 같습니다.

F = μmg

마찰력은 μ(마찰계수)*m(질량)*g(중력)으로 나타낼 수 있습니다. 여담이지만 면적이 넓다고 마찰력이 큰건 절!대! 아니니 어디가서 무식한 티 내지맙시다. 운동 마찰력은 정지 마찰력 계수보다 작기 때문에 F가 작은 것입니다.

흠. 강의가 약간 옆길로 샌 것 같네요. 저 지긋지긋한 InitNx함수를 이제 마무리 하겠습니다.

Create*함수들은 물체를 생성하는 함수입니다. 각 함수에 대해서는 바로 다음에 알아보도록 하겠습니다. 여기서 gGroundPlane은 바닥을 의미합니다. 3D 공간의 지구라고 생각하면 좋을 것 같습니다.  UpdateTime은 이전 프레임과 현재 프레임의 시간 차를 측정하는 함수 입니다. StartPhysics는 물리 연산을 시작하는 함수 입니다. 본격적인 계산에 앞에서 엔진에 삽입된 물체들의 물리 연산과 시간 차 함수의 초기화를 위해서 한번씩 호출했습니다.




위의 함수는 라이브러리를 해제하는 함수입니다. 비교적 간단하게 해제가 가능합니다. 먼저 현재 엔진에서 계산하는 물리 연산을 마무리 할 수 있도록 대기합니다. 그리고, Scene을 삭제하고, SDK의 메모리를 해제하는 순서로 진행됩니다.

이제 본격적인 엔진 제어 코드가 나옵니다. 심호흡 한번 하고 진행하도록 하겠습니다.

위의 코드는 3D 공간의 물체를 생성하는 함수 입니다. 사실, 물리 연산은 굉장히 복잡합니다. 그래서 복잡한 모형일 수록 계산이 복잡합니다. 이를 빠르게 처리하기 위해서 복잡한 모양의 모델을 단순한 모델로 취급하여 계산합니다. 위의 객체(평면, 육면체, 구, 캡슐)들은 그 때 사용할 수 있는 기본적인 단순한 모델을 말합니다.
먼저 평면을 살펴보겠습니다. 평면은 평면모양의 기본적인 설정을 가지고 있는 Descripter와 충돌에 관여하는 물체라는 의미의 액터(Actor) Descripter를 이용해서 생성합니다.

박스와 캡슐, 구는 생성하는 방식이 비슷합니다. 하지만 평면에는 없는 Body Descripter를 생성하는 것을 볼 수 있는데, 이는 움직일 수 있는 객체를 뜻합니다. 박스는 dimension이라는 멤버를 통해 사이즈르 설정할 수 있습니다. 캡슐, 구도 고유의 멤버를 가지고 있습니다. 한번 찾아보세요~ 쉽게 찾을 수 있을 겁니다.

density는 밀도를 의미하는 것으로, 물체의 무게를 간접적으로 알려줍니다. 물체의 무게는 밀도*부피이기 때문에, 크기가 설정되면 물체의 무게를 알 수 있습니다.

다음은 물리 엔진에서 가장 중요한 프레임 시간 측정 합니다. 이 함수를 정확히 구해야, 정확한 물리 결과가 나옵니다.

QueryPerformance*함수들은 Windows.h에 정의되어 있는 함수입니다. 운영체제에서 제공하는 시간측정함수로 높은 정밀도를 보장하는 함수입니다. static 변수를 이용해서 이전 프레임의 시간과 현재 프레임의 시간을 측정하여 시간차를 구하게 됩니다.

다음으로 물리 엔진의 연산 시작과 끝을 담당하는 함수를 살펴보겠습니다.

StartPhysics함수는 UpdateTime함수를 이용해 물리 연산을 수행하는 부분을 보이고 있습니다. gScene의 멤버 함수는 simulate 함수를 호출하여 시뮬레이션을 시작합니다. 멀티 쓰레드 방식으로 수행되기 때문에 바로 리턴이 됩니다. 시뮬레이션의 종료 여부는 gScene의 멤버인 fetchResults 함수를 이용해서 알 수 있습니다. fetchResults 함수의 두번째 인자가 true이면 블로킹함수로 동작하여 시뮬레이션이 끝날 때까지 리턴하지 않습니다. 만일 fasle이면 넌-블록킹함수로 동작합니다. while 루프에 시뮬레이션과 동시에 수행할 작업(AI, Network 등)을 넣으면 됩니다.

다음은 물체를 제어하는 방법을 설명하겠습니다.

먼저 키조작에 의해 물체에 외력을 가해 움직이게 하는 부분입니다.

내용은 많지만 잘 살펴보면 ProcessForceKey함수만 확인하면 됩니다. ApplyForceToActor 함수는 현재 선택된 액터에 x,y,z축 방향으로 gForceStrength 만큼 외력을 가하는 것입니다. KeyDown과 KeyUp함수는 callback.h/cpp에 있는 KeyboardCallback/KeyboardUpCallback함수에서 호출되는 함수입니다. KeyUp함수에서는 프로그램의 옵션을 설정하는 키들을 설정합니다.

ApplyForceToActor 함수는 다음과 같습니다.

방향과 크기, 그리고 시간 차를 이용해서 외력벡터(forceVec)을 만듭니다. 그리고 이를 액터의 addForce 함수를 이용하여 외력의 합을 구합니다.

자 이제 남은 부분은 부수적인 부분이 남았군요.. 힘내서 마저 포스팅 해보겠습니다.

다음은 r키를 누르때 동작하는 액터 바꾸기 입니다.

IsSelectable함수는 인자로 넘어온 액터가 움직일 수 있는 액터인지를 확인하는 함수입니다. NX_TRIGGER_ENABLE 인자가 있거나 static 설정이 되어 있는 액터는 움직일 수 없기 때문에 이를 체크하는 함수입니다. SelectNextActor함수는 IsSelectable함수를 이용하여 선택가능한 다음 물체를 선택하는 함수입니다. 선택된 액터는 키조작에 의한 외력의 영향을 받습니다.

위의 함수는 렌더링과 관련된 함수입니다. RenderActors 함수는 Scene에 있는 모든 액터를 화면에 렌더링하는 역할을 합니다. 함수이름에 "Nb"가 있으면 개수를 가져오는 함수임을 알 수 있습니다. DrawForce 함수는 물체에 가해지는 외력의 모습을 렌더링합니다.
제일 중요한 함수가 RenderScene입니다. 렌더링하기 전, 이전 프레임의 물리 연산 결과를 받습니다. 그 다음 사용자의 입력을 수행하고, 입력에 대한 물리 연산을 수행합니다. 그 사이에 이전 프레임에서 행했던 물리 연산의 결과를 화면에 렌더링하는 순서를 가지고 있습니다.
최근에는 게임 엔진들이 멀티 쓰레딩이 기본이라, 이런식으로 한 작업이 수행될 때 다른 작업을 수행할 수 있도록 하는 구조가 보편적으로 사용된다고 합니다.


프로그램 수행에 필요한 코드를 모두 설명했습니다. 처음하는 지라, 순서가 뒤죽박죽인 듯한 느낌이 강하네요. 차츰 더 나아 질 겁니다. 다음은 실행 결과입니다.

그림 2. 실행 결과

r : 액터 선택하기
p : 멈추기
x : 그림자
b : 디버깅 Wireframe 그리기
wasdqz : 카메라 움직이기
ikjlum : 물체 움직이기


다음은 이번 강좌에서 사용한 주요 함수, 클래스들입니다. 천천히 읽어보면서 기억을 상기하는 것도 괜찮을 것 같습니다.


소스코드는 이전 포스트에 올려놓았습니다. nVidia에서 제공하는 소스와 살짝 다릅니다.(사실 귀찮은 기능들은 다 빼버린 지라... 특히 HUD 부분이 빠져있습니다.) 기본적으로는 거의 같은 소스입니다. 참고하세요~^^
posted by 스펜서.