[PhysX] 4화 Actor

Grphics 2009. 5. 11. 14:25
그림 1. 실행화면


오늘만 2번째 강좌를 올리네요. 1화는 내용이 많아서 정말 정리하기가 어려웠는데, 뒤로 갈 수록 짧아지네요~ 정리하는 입장에서는 좋네요~

이번에 배울 내용은 Actor 입니다. Actor는 3D 공간의 충돌 개체를 의미합니다. 여기에는 세가지 종류가 있습니다.
    1. Dynamic
    2. Kinematic
    3. Static
딱 이름만 봐도 대충 알 수 있겠죠? 아니라고요? 사실 저도 아니에요...-_-

Dynamic. 이름이 참 다이나믹하죠? 이 액터는 힘에 의해서 움직일 수도 있고, 충돌도 가능한 객체입니다. 지금까지 우리가 만든 액터들이 모두 Dynamic 액터입니다.

Knematic. 낯선 단어네요. 운동학적인 이라는 뜻이라네요. 이 액터는 한 위치에서 다른 위치로 이동(transfer)는 가능하지만, 힘에 의해서 움직일 수 있는 객체는 아니라고 하네요.
PhysX 도움말에 다음과 같이 나와있네요.
Kinematic actors are special dynamic actors that are not influenced by forces (such as gravity), and have no momentum. They are considered to have infinite mass and can be moved around the world using the moveGlobal*() methods. They will push regular dynamic actors out of the way. Kinematics will not collide with static or other kinematic objects.
Kinematic actors are great for moving platforms or characters, where direct motion control is desired.
모두들 영어는 잘 하실테니.... ㅈㅅㅈㅅ 대충 번역하자면,  다음과 같습니다.
Kinematic 액터는 힘에 영향을 받지 않고, 운동량을 갖지않는 특별한 Dynamic 액터입니다. 무한 질랑을 가진 물체로 간주하며, moveGlobal*() 계열의 함수로 움직일 수 있습니다. 일반적인 Dynamic 액터를 밀 수도 있습니다. 하지만 다른 Static 이나 Kinematic 물체와는 충돌하지 않습니다.  Kinematic 액터는 움직이는 플랫폼이나 캐릭터와 같이 직접적인 조작이 필요한 곳에서 쓰면 좋습니다.
Static. '정적인' 이라는 뜻을 지닌 단어입니다. 이 액터는 어떤 방법을 쓰더라도 움직일 수 없는 녀석입니다. 즉 고정된 물체를 의미합니다. 바닥이나 건물을 생각하시면 좋겠네요.

이 부분도 사실 설명할 부분이 없네요. Kinematic과 Static은 Dynamic 액터를 만드는 방법에서 약간만 바꾸면 됩니다. 박스는 Dynamic, 캡슐은 Kinemaitc, 구는 Static 입니다. Dynamic 함수와 다른 부분을 Kinematic, Static 색으로 표시하겠습니다.

소스 1. Dynamic, Kinematic, Static의 차이
NxActor* CreateDynamicActor()
{
    NxActorDesc actorDesc;
    NxBodyDesc bodyDesc;

    NxBoxShapeDesc boxDesc;
    boxDesc.dimensions = NxVec3(1.0f, 1.0f, 1.0f);
    actorDesc.shapes.push_back(&boxDesc);

    actorDesc.density = 1.0f;
    actorDesc.globalPose.t = NxVec3(6.0f, 1.0f, 0.0f);
    actorDesc.body = &bodyDesc;
   
    NxActor* pActor = gScene->createActor(actorDesc);
    assert(pActor);

    return pActor;
}

NxActor* CreateKinematicsActor()
{
    NxActorDesc actorDesc;
    NxBodyDesc bodyDesc;

    bodyDesc.flags |= NX_BF_KINEMATIC;

    NxCapsuleShapeDesc  capDesc;
    capDesc.radius = 1.0f;
    capDesc.height = 1.5f;
    actorDesc.shapes.push_back(&capDesc);

    actorDesc.density = 1.0f;
    actorDesc.globalPose.t = NxVec3(0.0f, 3.0f, 0.0f);
    actorDesc.body = &bodyDesc;
   
    NxActor* pActor = gScene->createActor(actorDesc);
    assert(pActor);

    return pActor;
}

NxActor* CreateStaticActor()
{
    NxActorDesc actorDesc;
    //NxBodyDesc bodyDesc;

    NxSphereShapeDesc sphereDesc;
    sphereDesc.radius = 1.8f;
    actorDesc.shapes.push_back(&sphereDesc);

    //actorDesc.density = 1.0f; <- static 이기 때문에 무게가 필요없다.
    actorDesc.globalPose.t = NxVec3(-6.0f, 5.0f, 0.0f);
    actorDesc.body = NULL;
   
    NxActor* pActor = gScene->createActor(actorDesc);
    assert(pActor);

    return pActor;
}

소스 뷰어로 보고싶으시면 여길...
posted by 스펜서.

[PhysX] 3화 Compound

Grphics 2009. 5. 11. 12:03
그림 1. 실행 화면

오늘 살펴볼 내용은 여러개의 기본 도형을 혼합하여 사용하는 방법을 알아보겠습니다. 대부분의 경우, 기본적인 도형으로는 충분하지 않습니다. 그래서 위의 그림처럼, 기본 도형이나 삼각형 메쉬를 혼합하여(Compound) 사용합니다. 이번 강좌에서는 지금까지 배운 도형들을 혼합하여 사용하는 방법에 대해서 알아보겠습니다.

아마, 지난 강의부터 궁금해 하던 부분이 있었을 꺼에요. 없나요..?ㅋ 저는 다음 한줄이 너무 궁금했습니다.
actorDesc.shapes.push_back(&sphereShapeDesc);
저는 위에 한줄에서 왜 shapes 일까가 궁금했습니다. 왜 복수일까? 라는 생각을 했었습니다. 결국 이제서야 그 궁금증을 풀게되었네요. ShapeDesc를 만들어서 shapes에 추가만 해주면 됩니다. 참 쉽죠잉~? 죄송합니다(- -) (_ _) (- -)

자 그러면 소스 나갑니다. 이번 소스는 제가 그다지 설명드릴 부분이 없네요. 중요한 부분만 살펴보겠습니다.

소스 1. CreatePrimaryMultiShape() 함수


위에 소스에서 가장 중요한 부분은 이 부분이 아닐까 합니다.

    // 액터의 도형 리스트에 추가
    actorDesc.shapes.push_back(&boxShape);
    actorDesc.shapes.push_back(&capsuleDesc);
    actorDesc.shapes.push_back(&sphereShape);

shapes에 우리가 만든 도형들의 Desc를 추가해주는 겁니다. 아 그리고 한 가지 부분이 더 있네요.
    ...
    boxShape.localPose.t = NxVec3(1.5f, 0.0f, 0.0f);
    ...
    capsuleShape.localPose.t = NxVec3(0.0f, 0.0f, 0.0f);
    ...
    sphereShape.localPose.t = NxVec3(-1.5f, 0.0f, 0.0f);
    ...
바로 이 부분입니다. 로컬 좌표의 위치를 이동시키는 겁니다. 즉, 결과적으로 구성될 메쉬의 중심점으로 부터 이 도형을 어느 곳에 위치시킬 것인지를 정하는 부분입니다. 로컬 좌표와 글로벌 좌표의 관계는 나중에 다시 한번 소개하도록 하겠습니다.

자, 마지막 부분입니다. 이 소스를 마지막으로 강좌를 마쳐야겠네요~

소스 2. CreateMeshMultiShape() 함수


아참, 아마 이렇게 만들어 놓고 돌리면 CreateMeshMultiShape() 함수로 만든 도형은 움직이지 않을꺼에요. 왜냐하면, 너무 무겁기 때문이죠.
NxReal    gForceStrength = 40000;  // 20000에서 40000으로
위와 같이 힘의 세기를 변경하면 될꺼에요. 무거워서 안 움직인거뿐이에요~

posted by 스펜서.

인생은 아름다워

끄적끄적 2009. 5. 11. 01:41
인생은 아름다워
감독 로베르토 베니니 (1997 / 이탈리아)
출연 로베르토 베니니, 니콜레타 브라시, 조르조 칸타리니, 귀스티노 두라노
상세보기

사실 영화를 좋아하는 편은 아니다. 아니, 찾아서 보는 편은 아니다. 그래서 사실 유명한 우리나라 영화도 못본 영화가 많다. 그러던 찰나에 내가 자주보는 미드에서 영화광이 하나 나오지 않는가? 그 캐릭터는 어떤 상황이든 영화에 비유하곤 했는데, 그게 왠지 재미있어 보여서 영화를 한편 볼까 싶었다.

일요일 늦은 저녁, 라디오스타를 시청하고 있었다. "출연진의 인생을 영화의 OST로 표현하자면 어떤 영화일 것 같느냐" 라는 질문에 김구라가 대답한 영화가 "인생은 아름다워(Lift is beautiful)" 이였다. 어디서 많이 들어본 영화였지만, 한번도 보지 못했던 영화였다. 왠지 제목도 철학적이고 해서, 보기로 했다.

이 영화는 로베르토 베니니 감독의 1997년에 만든 영화이다. 감독과 주인공을 같이 한 영화이다. 1998년 제51회 칸영화제 심사위원 대상을 수상하였고, 1999년 제71회 아카데미상 7개 부문 후보에 올라 남우주연상·외국어영화상·음악상을 수상한 영화이다.


우연히 지나가다가 만난 아름다운 여자(도라)에게 자신은 왕자라고, 그리고 아름다운 여자에게 공주라고 부르는 한 남자. 근심과 걱정이라는 단어가 그에게는 없는지 항상 즐겁고 밝게 사는 남자(귀도)이다.

귀도는 약혼자가 있는 도라와 사랑에 빠지고, 이 둘은 마을에서 도망쳐서 단란한 가정을 이룬다. 작은 책방을 운영하면서 사랑스러운 아들(조슈아)과 함께 평화로운 생활을 하고 있었다.

하지만, 독일의 나치가 이탈리아를 점령하게 되면서 이들의 평화로운 생활은 끝이 났다. 나치는 유대인 학살 정책을 펴기 시작하면서, 귀도와 그 아들을 잡아가게 되고, 이를 알게된 도라 역시 함께 유대인 수용소에 들어가게 된다.


귀도는 아들을 안심시키기 위해서 게임을 하고 있다고 믿게한다. 탱크를 좋아하는 아들에게 1000점을 얻게되면 탱크를 상으로 준다는 거짓말을 통해 아들을 안심시키고, 안전하게 보호하려한다.


비참한 수용소 생활 속에서도 아들이 게임을 한다고 믿게 하기 위해서 외줄타는 듯한 생활을 한다. 보는 내내, 마음을 졸이면서 봤다. 과연 나도 내 아들을 보호하기 위해서 저렇게 생활할 수 있을까 하는 생각이 들었다.


마침내 독일이 패망하고, 유대인 수용소는 증거를 없애기 위해서 유대인을 죽이고 있었다. 귀도는 아내와 아들을 구하기 위해서 아들을 우편함(?)에 숨긴다. 아내를 구출하기 위해, 여자 수용소로 몰래 들어가다가 독일군 병사에게 걸려 처형당하러 가게된다. 아마 이 영화에서 이 장면이 명장면 같다.


자신이 죽으러 가는데도 불구하고, 끝까지 게임이라고 믿게 하고자, 웃으면서 걷는 모습. 이 장면을 보다가 울컥했다. 얼마나 사랑하게 되면, 죽음 앞에서도 초연해 질 수 있을까...? 정말 진정한 남자인 것 같다. 이러한 아버지의 희생 덕분에 아들은 살아나게 되고, 뒤늦게 찾아온 미군에 의해 구출된다.


탱크를 타고 가던 중, 엄마와 재회를 하고 엄마에게 1000점을 따서 게임에 이겼다고 말한다. 아버지 덕분에 끔찍한 수용소의 생활을 즐겁고 재미있는 추억이 된 셈이다. 그러면서, 나래이션은 다음과 같이 말한다.

' 이것이 나의 이야기다. 아버지가 희생한 이야기, 그것이 아버지가 주신 귀중한 선물이였다.'

인생이 지치고 힘들더라도 끝까지 웃으면서 자신의 사랑하는 사람을 위해 희생하는 모습. 이렇게 사는 삶은 언제나 아름답지 않을까?
posted by 스펜서.

[PhysX] 2화 Mesh Shape

Grphics 2009. 5. 10. 14:49
그림 1. 실행 화면

지난 시간에는 PhysX에서 제공하는 구, 캡슐, 박스, 평면 형태의 기본 모양에 대해서 살펴보았습니다. 이번에 공부할 내용은 임의의 형태를 가지는 충돌개체를 만드는 것입니다. 임의의 형태의 충돌 개체는 기본 도형으로 처리하기 힘든 개체를 표현할 때 사용합니다. 하지만 Mesh-Mesh 충돌검사는 비용이 비싼 계산입니다. 그렇기 때문에 되도록 단순하게 만들어야 합니다.

임의의 형태를 가지는 메쉬를 만드는 데는 두가지 방법이 있습니다. 첫번째는 Convex Mesh를 생성하는 방법입니다. 여기서 Convex는 볼록다각형을 의미합니다. 이에 반대말은 Concave입니다.

그림 2. Convex와 Concave

Concave는 3D 그래픽스에서 많은 단점을 가집니다. 먼저 내부를 색칠해야 하는 문제에 있어서도 Convex 도형에 비해서 알고리즘이 복잡합니다. 그래서 보통은 Concave 도형을 쪼해서 Convex 도형으로 만들어 Convex 도형의 집합으로서 처리합니다.

아무튼 Convex Mesh는 구현하기가 쉽습니다. 왜나하면 정점(Vertex)정보만 넘겨주면 알아서 메쉬를 구성하기 때문이죠. 하지만 이에 상응하는 단점이 있는데요, 그건 정점은 최대 256개를 넘어선 안된다는 겁니다. 즉, 복잡한 Convex Mesh는 만들지 못하는 겁니다.

百聞不如一見, 소스를 봅시다.

소스 1. CreateConvexMesh() 함수

Convex Mesh를 만드는데는 다음과 같은 순서로 생성합니다.
  1. 정점 정보 만들기
  2. NxConvexMeshDesc 객체 생성하기
  3. NxConvexMeshDesc(gPhysicsSDK->createConvexMesh) 객체를 바탕으로 ConvexMesh 생성하기
  4. 생성된 NxConvexMesh(.meshData)와 NxConvexMeshDesc(.userData)를 이용해서 NxConvexMeshShapeDesc 객체 생성하기
  5. NxBodyDesc(.body)와 NxConvexMeshShapeDesc(.shapes)를 이용해서 NxActorDesc 생성하기
  6. NxActorDesc(gScene->createActor)로 Actor만들기
위에서 괄호안에 있는 것은 생성하고 하는 객체에 대응되는 멤버나 함수를 적은 것입니다.

참 쉽죠..?(@_@)

두번째 방법은 Triangle Mesh를 생성하는 방법입니다. 이 방법은 개수의 제한은 없지만, 생성하는 방법이 조금 까다롭습니다. Convex Mesh는 정점 정보만 받았다면, 이 방법은 정점과 인덱스 정보도 주어야 합니다.

잠깐, 정점과 인덱스와의 관계를 설명하겠습니다. 정점(Vertex, 복수는 Vertices)는 말 그대로, 3차원 공간에서의 위치입니다(주로 3D에서의 위치를 Vertex라고 하고, 2D에서는 Point라고 합니다). 인덱스(Index, 복수는 Indices)는 폴리곤의 정보 - 폴리곤은 삼각형, 사각형 등 어떤 기본 도형이든 될 수 있다 - 를 저장하는 개체입니다. 예를 볼까요...?

그림 3. 정점과 인덱스의 관계(폴리곤은 삼각형)

정점을 정의하면 이는 단순히 공간상의 4개의 위치를 의미하는 것입니다. 인덱스는 정의된 정점의 번호를 나열하여, 하나의 다각형을 만드는 것입니다. 위의 예에서 0, 1, 2라고 정의했기 때문에 0, 1, 2번 점을 이어서 하나의 삼각형을 만드는 것입니다. 마찬가지로 3, 0, 2도 해당하는 인덱스에 있는 정점을 이어서 삼각형을 만든 것입니다.

하지만 여기에도 규칙이 있어야 합니다. PhysX에서는 노말을 계산하기 위해서 다음의 공식을 사용합니다.
Normal = (v1 - v0) x (v2 - v0)
즉, 만들고자 하는 면의 방향을 바라본 상태에서, 반시계 방향으로 삼각형을 만들어야 합니다. 그렇지 않으면 Normal이 뒤집혀서 계산될 수 있습니다. 이러면, 제대로된 충돌 검사가 이루어질 수 없죠. 이 부분은 모눈종이에 정점을 적어서 한번 생각해보기시 바랍니다.

그리고, 정점을 넘길 때, 같은 위치를 가리키고 있는 정점을 두개 이상 선언하면 안됩니다. 예를 들어 (0, 0, 0) 위치가 배열에 0번째에 있었는데, 배열의 10번째 위치도 (0, 0, 0)이라면, PhysX는 이러한 중복을 체크하지 않기 때문에 예기치 못한 결과를 야기할 수 있습니다.

자, 그럼 서론은 마무리 짓고, 본격적인 소스를 보겠습니다.

소스 2. CreateTriangleMesh() 함수


기본적인 순서는 Convex Mesh와 매우 유사합니다.
  1. 정점 정보 만들기
  2. 인덱스 정보 만들기
  3. 정점 정보(.numVerices, .pointStridebytes, .points)와 인덱스 정보(.numTriangle, .triangleStrideBytes, .triangles)를 이용해서 NxTriangleMeshDesc 객체 생성하기
  4. NxTriangleMeshDesc(gPhysicsSDK->createTriangleMesh) 객체를 바탕으로 NxTriangleMesh 생성하기
  5. NxTriangleMeshDesc(.userData), NxTriangleMesh(.meshData)를 이용해서 NxTriangleMeshShapeDesc 객체 생성하기
  6. NxTriangleMeshShapeDesc(.shapes)와 NxBodyDesc(.body) 객체를 이용해서 NxActorDesc 생성하기
  7. NxActorDesc(gScene->createActor)를 이용해서 NxActor 생성하기
인덱스는 NxU32 말고 NxU16을 사용할 수 있다. 이럴 경우라면, 폴리곤의 정점이 2^16개를 넘어서지 않는 경우일겁니다. 이럴 때는 위의 소스와 약간 다른데, 그때의 성정하는 방법은 주석으로 적어놓았으니 참고하세요.

자 그럼, 마지막으로 생성한 녀석들을 삽입하기 위해서 InitNx()함수에 추가하겠습니다.
소스 3. InitNx()에 객체 추가하기

setGlobalPosition함수를 이용해서  Triangle Mesh를 (5, 0, 0)으로 이동시킨 코드입니다. 아마 쉽게 이해가 되실 겁니다.

자 이렇게 이제 여러분은 임의의 형태의 충동 객체를 만드실 수 있습니다. 축하합니다. > _<

ps. Triangle이나 Convex Mesh를 생성할 때, 같은 모양의 객체를 여러개 만들고자 할 때는 NxTrangleMeshShape이나 NxConvexMeshShape을 여러개 생성하여 만드시길 바랍니다.



posted by 스펜서.

질러라!!! Wish List

끄적끄적 2009. 5. 9. 12:00
아~! 요즘 사고 싶은 물건들이 너무 많아졌다. 종합소득세 공제? 뭐 그런걸로 꽁돈이 생겨서 >_< 행복한 고민을 하고 있다. 사고 싶은 걸 정리해봐야 겠어..-_-ㅋ


1. 선풍기 : 곧 여름이 다가오는데, 에어콘은 없으니 선풍기로 만족해야지..ㅠ_ㅠ 이건 리모콘이 있는 모델. 가격은 32,500원.

2. Filco Majestouch Non-Click 103 Black : 기계식 키보드는 정말 오래전부터 사고 싶었다. 단지 너무 비싸서, 입맛만 다시고 있었는데, 옆 연구실에 가지고 있는 사람이 있어서 써봤는데, ㅠ_ㅠ 감동! 가격은 135,000원

3. 소프트 다트 셋트 : 친구랑 가끔 바에 가서 다트를 던지곤 하는데, 정신수양에 좋은 것 같다(-_-). 솔직히 이건, 사고 싶은거긴 하지만, 그닥 땡기진 않다. 가격은 140,000원

4. Canon 580EX II : 카메라 살 때부터 사려고 마음먹고 있었는데, 망할 엔고 현상으로 가격이 50%이상 뛰었다. 젠장. 예전에 가격조사 할때는 41만원이였는데, 지금은 60만원 가까이 한다. 이건 중고로 사야겠다.

5. 탐론 17-50 2.8f : 이 렌즈도 플래쉬와 같이 사려고 했던건데, 이것 역시 가격에 거품이 많은 상태. 중고로 사던지 기다려야겠다.


posted by 스펜서.

[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 스펜서.

[PhysX] 1화 Primary Shape #1

Grphics 2009. 4. 28. 20:13
지난주부터 PhysX를 공부하면서 정리하려고 했는데, NVIDIA에서 제공하는 튜토리얼이 있긴 하지만 정리하려고 했더니 만만치 않네요. 특히 PhysX는 물리 엔진이기 때문에 렌더링과 관련 라이브러리가 없답니다. 그래서 초반에 셋팅해야 하는 것들이 만만치 않게 많아요.

이번 포스트는 nVidia에서 제공하는 튜토리얼을 기반으로 하고 있습니다. 다만, 단순 번역이 아니라 소스 코드의 순서나, 설명을 제 나름대로 재구성해보았습니다.

만만치 않은 양이지만, "시작이 반이다"라는 말이 있듯이, 한번 달려보죠. 아자!


아시다시피 PhysX는 물리엔진입니다. 그렇기 때문에, 화면에 출력하는 그래픽과 관련된 API가 전무합니다. 사실 그래야 물리엔진이겠죠. 그래서 PhysX를 사용하기 위해서는 렌더링을 할 수 있는 API를 사용해야 합니다. 저는 OpenGL을 사용하도록 하겠습니다. DX도 가능하지만, DX 보다는 OpenGL이 간단하게 프로그래밍하기 좋기 때문에, OpenGL을 사용하도록 하겠습니다.

OpenGL에는 GLUT라는 라이브러리가 있습니다. 이 라이브러리는 DXUT와 마찬가지로 손쉽게 OpenGL을 코딩할 수 있도록 해주는 프레임워크 라이브러리입니다. 이 라이브러리가 없으신 분은 다운 받아 설치합시다.

혹시나 PhysX를 설치하지 않으신 분들은 여기에 강좌가 있으니 설치를 먼저 하세요.

강좌에 사용된 소스코드입니다. 같이 보시면서 공부하시면 도움이 될 것 같습니다. nVidia에 있는 소스와 약간 다릅니다.


이 소스는 기본적으로 PhysX와 관련된 폴더가 환경설정에 추가가 되어 있어야 합니다. 포함파일(include)폴더들과 라이브러리파일(lib) 폴더 경로가 설정되어 있어야 합니다. 또 "PhysX공용"이라고 되어 있는 곳의 소스는
  • (NVIDIA PhysX)\v2.8.1\TrainingPrograms\Programs\Shared_Source
  • (NVIDIA PhysX)\v2.8.1\TrainingPrograms\Programs\Shared_Source\User_Reports
에 있는 소스입니다. 참고하시기 바랍니다.


자 이제 시작하겠습니다. 먼저 메인 함수부터 만들어봅시다.

callback.h에는 주로 렌더링에 필요한 기반 코드와 카메라 제어를 위한 마우스, 키보드 코드가 있니다. physx.h에는 PhysX 라이브러리를 사용하는 물리 엔진 함수와 객체를 렌더링하는 코드, 객체를 제어하기 위한 키보드 입력을 위한 코드가 있습니다.

먼저, InitGLUT함수를 이용해서 렌더링을 위한 윈도우 생성과 OpenGL 초기화를 할 예정입니다. InitNx함수에서는 PhysX의 초기화를 진행하겠습니다. RunGLUT함수가 실행되면 루프를 돌면서 이벤트 처리, 물리 연산, 렌더링이 진행됩니다. 프로그램이 종료되면 ReleaseNx함수가 호출되면서 PhysX에서 사용하던 메모리 해제가 진행됩니다.

1. InitGLUT함수에서는 GLUT에서 필요한 콜백함수를 등록합니다. glutInit함수를 통해 GLUT 라이브러리를 초기화합니다.
2. glutInit*()함수들을 이용해서 우리가 생성하고자 하는 윈도우의 렌더링모드를 설정하고 장의 위치 및 크기를 설정합니다.  마지막으로 glutCreateWindow함수를 이용해서 창을 생성합니다.
3. GLUT에서 필요한 콜백함수를 등록하여 우리가 구현한 함수가 해당하는 이벤트가 발생할 때 실행될 수 있도록 합니다.

먼저 glutReshapeFunc 함수에 등록한 ReshapeCallback 함수부터 구현해봅니다. WM_SIZE가 발생할 때 실행되는 함수 입니다.  먼저 전역변수로 gWindowWidth, gWindowHeight를 만듭니다.  이 변수들은 나중에 카메라 설정때 Aspect Ratio를 구하기 위해서 사용됩니다. 이 함수에서는 창의 크기가 바뀔 때 마다 창에 꽉 차게 렌더링을 하기 위해서 Viewport를 설정합니다.  Viewport를 생성하고 카메라와 조명을 셋팅합니다.


ApplyLight함수는 조명을 설정합니다. 조명은 방향성 조명으로 설정했습니다. glColorMaterial 함수를 이용해서 메쉬에 적용된 색이 Ambient, Diffuse Light에 반응하도록 했습니다. ApplyCamera함수는 카메라을 설정합니다. 전역변수로 설정되어 있는 gCamera* 변수를 이용해서 pos, look, up 벡터를 설정합니다.

이제 좀 더 어려운 부분인 카메라 제어 부분을 살펴보겠습니다. 카메라 제어를 위해서 키보드 관련 콜백함수와 마우스 콜백 함수를 사용합니다.

이 부분이 조금 헷깔릴 수 있습니다. gKeys 배열은 physx.cpp에 정의되어 있는 전역변수입니다. 이를 callback.h에서 사용하기 위해서 physx.h에서 extern으로 전역변수 선언을 한 후, callback.cpp에서 사용한 것입니다. 사실 한 파일에 몽땅 코딩해 버리면 이렇게 할 필요가 없지만, 좀 정리하면서 코딩을 하기 위해서 분리해 놓은 것입니다. 여러분은 extern을 이해할 수 있을 거라 보고, 설명은 넘어가겠습니다. 
KeyboardCallback함수에서는 키보드를 눌렀을 때 호출되는 함수입니다. 이 함수가 호출되면 해당하는 키가 눌렸다는 것을 gKeys배열에 표시합니다. 만약 esc키를 누르면, 프로그램을 종료합니다. KeyDown함수는 physx..h/cpp에 선언된 함수로 PhysX에서 키를 눌렀을때, 하고 싶은 이벤트를 구현한 함수입니다.
KeyboardUpCallback함수는 키를 땠을 때 호출되는 함수입니다. 키를 땠을 경우, gKeys 배열에 이를 표시합니다.

ProcessCameraKey함수에서는 gKeys 배열에 기록된 내용을 토대로 카메라의 위치를 이동합니다. 여기서 gCameraSpeed는 카메라의 움직임이 얼마나 빠르게 동작하는지를 의미합니다. deltaTime은 이전 프레임과 현재 프레임의 시간 차를 의미합니다. 정확하게 코딩하려면 시간 차를 구해서 적용해야 하지만, 이번 강좌에서는 약식으로 넘어가도록 하겠습니다.

카메라 제어 코드의 도식(w를 눌렀을 경우)

그림 1. 카메라 변수들 간의 관계

위의 그림은 ProcessCameraKey 함수에서 사용된 공식을 그림으로 나타낸 것입니다. 카메라의 위치는 gCameraPos이고, w를 눌렀을 경우, gCameaForward 방향으로 deltaTime*gCameraSpeed만큼 이동하게 됩니다. s를 누르게 되면 그 반대로 이동하게 하면 될 것입니다. a와 d는 gCameraForward대신 gCameraRight를 적용하면 좌우로 움직일 수 입니다.

다음은 마우스 쪽의 코드를 살펴보겠습니다.

MouseCallback함수는 마우스를 클릭한 지점을 전역변수인 mx, my에 저장합니다. MotionCallback함수는 드래깅 할 때 호출됩니다. 드래깅할 때 mx, my에서 x, y를 빼서, 클릭한 지점으로 부터 얼마만큼 떨어져있는지 dx, dy를 구합니다. 다음 계산을 위해서 gCameraForward를 정규화하고, gCameraRight를 gCameraForward와 y축을 외적하여 구합니다. 그리고 dx, dy를 각으로 변환하여 y축으로 dx만큼 회전하는 쿼터니온과, gCameraRight를 축으로 dy만큼 회전하는 쿼터니온을 생성합니다. 이를 이용해서 gCameraForward를 회전변환합니다. 쿼터니온은 그래픽스에서 자주 나오는 개념이니까 꼭 공부해보시기 바랍니다.

이제 callback.cpp는 거의 다 끝났습니다. 휴.. 남아 있는 함수를 보도록 하겠습니다.

void RenderCallback함수는 렌더링을 해야 할때 호출되는 함수이며, IdleCallback 함수는 렌더링이 끝난 후에 호출되는 함수 입니다. 사실 GLUT에서는 게으른 방식(??)으로 렌더링을 합니다. 즉, 렌더링되는 윈도우가 움직인다거나, 다시 그려야하는 경우, WM_PAINT가 호출되는 시점과 같은 시기에 렌더링이 됩니다. 즉, 매번 렌더링이 되는게 아니랍니다. 그래서 IdleCallback 함수에서 glutPostRedisplay()함수를 호출하여 다시 렌더링을 하도록 합니다. 이 함수는 마치 Invalidate()함수와 비슷합니다.

RenderCallback 함수에서는 glClear함수를 이용해 그래픽 버퍼를 초기화하고, 렌더링 하기 전, 카메라 제어에 대한 처리를 수행합니다(ProcessCameraKey(), ApplyCamera()).
그리고, PhysX의 객체 렌더링을 수행하는 함수인 RenderScene(physx.h/cpp)를 호출하고, glFlush함수로 큐에 대기중인 모든 OpenGL 명령을 그래픽카드로 보내고, glutSwapBuffers함수가 그래픽 버퍼를 교체합니다(더블 버퍼링, glutInitDisplayMode함수의 GLUT_DOUBLE인자가 더블버퍼링을 위한 인자입니다.).

휴... GLUT와 관련된 함수를 살펴보았습니다. 아마 당분간은 이 부분은 다시 살펴보지 않아도 될 것입니다. 초기화가 대부분이고 PhysX에 의해 바뀔 가능성이 있는 부분은 모두 함수로 빼서 physx.h/cpp에 정의 했으니까 callback.h/cpp는 가끔 살피면 될 것 같습니다.



포스트가 너무 길어졌네요. 다음 physX와 관련된 함수는 다음 포스트에서 이어가겠습니다.

"[PhysX] 1화 Primary Shape #2" 보기
posted by 스펜서.

Ubiquity 내가 만든 명령어

Web 2009. 4. 20. 13:34
파이어폭스 플러그인 중에 Ubiquity라는 녀석이 있다. 이거는 브라우저에서 Ctrl+Space로 커맨드창을 열어서 어떤 명령을 수행하게끔 도와주는 녀석이다. 자세한건 모질라랩에서 보면 될것 같다.

링크를 누르기는 귀찮고, 뭔지는 궁금한 사람들을 위해서 다음 동영상을 링크한다. 이걸 보면 "와~" 라는 감탄사가 연일 나올 수 있을 정도로 데모 영상을 잘 꾸며 놓았다.



Ubiquity를 설치하면 파이어폭스 주소창에 "about:ubiquity" 라고 입력하면 Ubiquity를 설정하는 창이 나온다.

그림1. Ubiquity의 모습

상단 메뉴에 보면 "Command Editor"라는 것이 있는데 이를 사용하면 내가 명령어를 직접 만들 수 있다. Ubiquity는 외국에서 만든거라 네이버나 다음과 같이 우리가 자주 쓰는 사이트에 대한 명령이 없다. 그래서 이런 명령이 필요하면 직접 만들어야 한다.

그래서 한번 만들어 보았다.
다음을 복사해서 "Command Editor"에 붙여넣기만 하면 다음과 같이 쉽게 네이버 검색을 할 수 있다.

그림2. Ubiquity를 이용해서 "naver 김연아"를 검색한 모습

다음은 내가 작성한 명령어이다. 버그가 있을지도 모르니 사용에 주의하자!ㅋ



앞으로도 계속 추가되는데로 업데이트 할 예정이다. 참고로 만일 검색하고 싶은 텍스트를 블럭설정하면 그냥 명령어만 쓰면 검색이 가능하다. 예를 들어, "소녀시대"를 네이버에서 검색하고 싶으면 "소녀시대"를 블럭설정하고 Ctrl+Space를 누르면 ubiquity에 다음처럼 명령어 뒤에 네모박스에 블럭설정한 키워드가 나온다.

그림3. 키워드를 블럭 설정했을 때의 모습

참고로 기존의 키워드가 있는 상태에서 다른 키워드를 입력해서 바로 엔터를 치는 경우, 키워드가 적용이 안되는 경우가 있다. 그럴때는 맨 뒤에 스페이스바를 누르거나 방향키를 누르면 해결된다.
posted by 스펜서.

IEEE 논문 저작권 첨부 문서

끄적끄적 2009. 4. 20. 10:43

IEEE CS에 출판되는 논문을 제출하려면 Copyright에 서명한 문서가 필요하다. 어떤 학회들은 이 문서를 링크를 하지 않아서 번거롭게 한다.

다음 주소에 가면 문서를 PDF, DOC, LATEX 버전으로 다운받을 수 있다.

IEEE Copyright Form


위의 PDF파일은 위 사이트에서 다운받은 파일이다. 이를 출력한 다음, 스캔해서 제출하면 되는 듯 하다.(지금 논문을 제출 중이라... 잘..-_-;)

여튼..! 모두 잘되기를.ㅋ
posted by 스펜서.

MySQL 백업 및 복구

Database 2009. 4. 20. 01:18
[MySQL 접속]
  mysql -hDB서버명 -uMySQL아이디 -pMySQL패스워드 데이타베이스명

[MySQL 백업하기]
  MySQL 명령어로 백업받기 :
  mysqldump -hDB서버명 -u유저명 -p패스워드 [백업할 테이터베이스명] > [외부로 저장할 파일명]
  ex) mysqldump -hdb1.guideline.co.kr -uDBuser -pPassword MyDB > MyDB.sql

 특정 테이블만 백업 할 경우 :
  mysqldump -hDB서버명 -u유저명 -p패스워드 [테이타베이스명] [특정테이블명] > [외부로 저장할 파일명]
  ex) mysqldump -hdb1.guideline.co.kr -uDBuser -pPassword MyDB MyTable > MyDB_MyTable.sql

[MySQL 복구하기]
  mysql -hDB서버명 -u유저명 -p패스워드 [복구할 테이터베이스명] < [외부로 저장된 파일명]
  ex) mysql -hdb1.guideline.co.kr -uDBuser -pPassword MyDB < MyDB.sql


posted by 스펜서.