[원본] http://kinesis.tistory.com/93


( 다이렉트X 의 셈플 예제 중 Direct3D 10 에 포함되어 있는 예제를 촬영한 화면이다. )
( 구슬에 반사되는 주변 환경이 깔끔하게 잘 묘사되어 있음을 확인 할 수 있다 )


Windows 7 에서 이번에 개인 학습 목적으로 (어차피 독학이지만) DirectX SDK (다이렉트 X 개발 도구) 를 설치해 보았다. 혹여 호환성 문제로 충돌이 있지 않을까 싶었지만, 아직 뭐 이렇다할 충돌은 전혀 보이지는 않는다.

설치가 완료 된 후 폴더를 확인 해 보니 include 해더 외의 Lib (라이브러리) 나 Developer Runtime 같은 녀석들은 대부분 x86 / x64 로 구분이 되어 같이 설치가 되어 각각의 환경의 개발을 할 수 있게끔 나뉘어져 있음을 확인했다.

그 외에도 DirectX Sample Browser 라고 별도의 DirectX (다이렉트 X) 의 예제 소스를 참고해 볼 수 있는 프로그램이 설치되어 각각 DX9 DX10 DX11 에 관련한 예제를 참고 할 수 있게끔 지원해주고 있음을 확인 할 수 있었다. 물론 제공 언어는 영어다. 화면은 밑의 스크린샷을 참고하시길.

 

DirectX Sample Browser


우선 좌측을 보면 show 부분에 어떠한 셈플을 볼 것인지 고를 수 있는 드롭박스가 위치해 있어서, Direct3D 9 / Direct3D 10 / Direct3D 11 등의 예제와 Audio / System / Tools / Articles 의 예제를 선택하여 참고해 볼 수 있게끔 제공하고 있으며, sort by 는 정렬방식을 고를 수 있도록 되어 있어 날짜순 / 이름순 / 난이도 순으로 정렬하여 참고할 수 있도록 되어있다.

오른쪽 화면은 그 중 폴리곤을 가장 눈으로 쉽게 판단할 수 있는 화면으로 담아보았다. ( 오른쪽 화면에서도 볼 수 있듯이 폴리곤은 3개의 점을 정점으로 면을 생성하고 채움으로서 그래픽을 표현하는 기법인데 화면상에서 박쥐의 폴리곤의 연결상태를 어렵지 않게 확인 할 수 있을 것이다. )

배움은 끝이 없다고 했던가? 가면갈수록 느끼는 것이지만 그 말이 너무나도 몸에 많이 와 닿는것 같다. 계속 스스로 발전하기위해 이런 저런 자료를 찾아보고 배워보려하지만 그 지식과 양이 너무 많아 내 머리속에 다 집어넣기는 다소 부족해보이지만 그래도 포기하지는 않아야 한다는 생각만이 남는다.

앞으로 이것저것 더 많이 만들어 보게 될 것이라 생각되고, 또 만들어 나갈 예정이지만, 내가 원하는 목표로 달성하는 그 날이 오기를 바래 본다.

DirectX SDK 의 용량이 500MB를 넘는지라 파일을 올리지는 못하고 다운받을 수 있는 링크를 밑에 첨부한다. 다이렉트 X 의 개발경험을 쌓아보고 싶은 분이나, 한번 체험해보고 싶으신 분은 다음 주소에서 다운받아 경험해보기를 바란다.
http://www.microsoft.com/korea/windows/directx/techinfo/default.mspx
또는
http://msdn.microsoft.com/ko-kr/directx/default(en-us).aspx

'Programming > DirectX' 카테고리의 다른 글

충돌처리 - 직선 교차점 이용  (0) 2009.12.19
Posted by 시그v

1. 충돌처리
· 직선 교차점 알고리즘

1.        직선을 정의하는 두 점(선분의 시작점과 끝점)이 모두 평면의 한쪽 방향에 위치하고 있다면 직선은 
절대로 평면과 교차할 수 없기 때문에 이 두 점이 폴리곤의 양쪽 방향에  존재하는지를 알아본다.
2.        폴리곤을 정의하는 평면과 직선의 교차점을 계산한다.
3.        평면과 직선의 교차점이 폴리곤 영역 안에 포함되어 있는지를 검사해서 폴리곤 영역에 포함될 경우 
충돌을 나타내고 그렇지 않을 경우 충돌하지 않은 것을 나타낸다.

· 평면과 직선의 교차
직선을 정의하는 두 점이 폴리곤을 정의하는 평면의 양쪽에 존재하는지를 알아보는 첫번째 과정은 상당히 쉽다. 
수학에서 정의하는 3차원 평면에 대한 방정식으로 유도할 수 있다.

평면의 방정식
Ax + By + Cz + D = 0
위의 식에서 A, B, C는 평면의 노말 벡터의  x, y, z 요소를 각각 나타내고 D는 폴리곤 위에 존재하는 임의의 
한 점을 방정식에 대입해서 구할 수 있는 값이다.

평면의 방정식의 특징은 평면위에 존재하는 어떤 점을 x, y, z에 대입하더라도 항상 0 이 된다. 이러한 특성을 
이용하여 충돌처리를 할 수 있는데 평면의 방정식의 특성상 평면의 뒤쪽에 존재하는 점을 방정식의 x, y, z에 
대입한다면 음수 값을 가지고 앞쪽에 존재하는 점을 대입하면 양수 값을 갖게 된다. 따라서 직선을 정의하는 두 
점이 평면의 양쪽에 존재하는지를 알아보기 위해서는 단순히 두 점을 평면의 방정식에 대입해서 그 부호가 서로 
다른지를 알아보면 된다.
그럼 다음 함수를 보자
bool LineCrossPlane( Vector *line, Vector &normal, float &D )
{
        float        sign1, sign2;

        sign1 = (normal.i * line[0].x + normal.j * line[0].y + normal.k * line[0].z + D);
        sign2 = (normal.i * line[1].x + normal.j * line[1].y + normal.k * line[1].z + D);

        if( sign1 * sign2 >= 0.0 )
         return false;
        
return true;
}
위의 말을 더 쉽게 풀이해 보면
한 삼각형(평면)의 Face Normal을 Nx, Ny, Nz라고 정의하구 삼각형(평면)의 한점을 Px, Py Pz 라고 한다면, 
삼각형의 노말과 그 삼각형에 속하는 한점을 DotProduct를 하면 0 이 된다.이 말을 평면의 방정식에 대입하면 
이렇게 된다...
Nx * Px + Ny * Py + Nz * Pz + D = 0 ........ (1)
(평면의 방정식 A*x + B*y + C*z + D = 0 )

여기서
A = Nx
B = Ny
C = Nz
D = -( Nx * Px + Ny * Py + Nz * Pz )
Px, Py, Pz 는 평면에 속하는 한점이다.

· 교차점 계산
직선을 정의하는 두 점이 평면의 양쪽에 존재한다면 반드시 직선과 폴리곤이 교차하는 교차점이 존재한다고 
할 수 있다. 이러한 교차점을 구하기 위해서는 평면과 직선을 모두 방정식으로 표현해야 한다.

평면의 방정식
Ax + By + Cz + D = 0

직선을 방정식으로 표현하는 방법은 여러 가지가 있지만 그 중에서 직선 위에 존재하는 모든 점을 가장 간편하게 
나타내는 방정식은 직선의 시작점으로 부터의 거리를 이용하는 방법이다.

P(d) = Po + dD
위의 식에서 D는 직선의 방향을 나타내는 단위 벡터이고 P(0)는 시작점을 그리고 d는 P(0)으로부터 D 벡터가 정의
하는 방향으로 떨어져 있는 거리를 나타낸다. 따라서 P(d)는 P(0)으로부터 d만큼 D방향에 떨어져 있는 직선 위의 
한 점을 나타낸다. 위에서 정의한 일반식을 x, y, z 요소로 분할하면 다음과 같음을 알 수 있다.

Px = xo + dDx
Py = yo + dDy
Pz = zo + dDz
위에서 Px, Py, Pz는 직선의 시작점 xo, yo, zo로부터 Dx, Dy, Dz 방향으로 d만큼 떨어져 있는 직선 위의 한 점에 
대한 x-, y-, z- 요소를 나타낸다.

그럼 임의의 한 점에 대한 평면의 방정식을 써보면
Axo + AdDx + Byo + BdDy + Czo + CdDz + D = 0
위와 같이 된다.
만일 직선 위의 한 점 Px, Py, Pz가 평면위에 존재한다면(교차점이라면) 이 방정식의 왼쪽 항은 0값을 갖게 된다. 
따라서 위의 방정식으로부터 교차점을 찾기 위해서 먼저 상수 d에 대해 정리하면

d(Adx + BDy + CDz) + Axo + Byo + Czo + D = 0                 이것을 다시 정리하면
d = -(Axo + Byo + Czo + D) / (ADx + BDy + CDz)

Dx, Dy, Dz는 직선의 방향을 나타내는 단위 벡터이고 A, B, C, D는 평면을 나타내는 방정식의 계수다. 또 xo, 
yo, zo는 직선의 시작점을 나타내기 때문에 모두 상수임을 알 수 있다. 따라서 아무 문제없이 위의 방정식으로부터 d값을 구할 수 있다.

¨ NOTE
만일 평면과 직선이 서로 평행이라면 위의 방정식의 분모는 0 이 된다. 그 이유는 위 방정식의 분모를 보면 
평면에 수직인 법선벡터(A, B, C)와 직선의 방향을 나타내는 벡터(Dx, Dy, Dz)에 대한 내적임을 알 수 있다. 
만일 평면과 직선이 평행이라면 평면에 수직인 법선 벡터와 직선의 방향 벡터가 이루는 각은 90도이다. 따라서 
내적 정의에 의해 0이 된다.

함수는 다음과 같다.
bool  LineIntersectPlane( Vector &point, Vector *line, Vector &normal, float &D )
{
        Vector line2 = line[1] – line[0];
        float        d, first, second;
        
        line2.Normalize();        // 단위벡터로 만든다.

        first = -( normal.i*line[0].x + normal.j*line[0].y + normal.k*line[0].z + D );
        second = ( normal.i * line2.i + normal.j * line2.j + normal.k * line2.k );

        if( second == 0 )
          return false;
        
        d = first / second;

        if( d < 0 )
          return false;

        point = (line[0] + (Vector)(line2 * d));
        return true;
}

· 폴리곤 영역에서 충돌이 일어났는지 판별
폴리곤 영역에서 충돌이 일어났는지 판별하기 위해서는 교차점과 폴리곤의 Vertex가 이루는 각의 합을 계산해서 
360도와 같다면 영역에 포함된다고 할 수 있다.

내적 공식
A · B = |A| |B| cosq
위에서 q는 두 벡터가 이루는 각을 나타낸다. 따라서  이 방정식을 q에 대해 풀면 다음과 같다.

q = cos¯¹ (A · B / (|A| |B|))
위에서 cos¯¹은 코사인 함수의 역함수를 나타낸다. 폴리곤의 버텍스로부터 벡터 A와 B를 계산하기 위해서는 
단순히 각 버텍스에서 교차점을 빼면 된다.

내적 계산
A·B = x1x2 + y1y2 + z1z2

함수로 표현하면
bool  IsPointBounding( Vector &point, Vector  *Poly, long VertexCount )
{        
float Angle = 0.0f;

const MATCH_FACTOR = 0.99;
Vector A, B;

for( long n = 0; n {
        A = (Vector)(Poly[n] – point);
        B = (Vector)(Poly[(n+1) % VertexCount] – point);
        Angle += acos( A.Dot(B) / (A.Mag() * B.Mag()));
}

if( Angle >= (MATCH_FACTOR * (2.0 * PI)) )        // 360도는 2PI
  return true;
        
return true;
}


위의 세가지 과정을 통합한 것을 함수화 하면
bool LineIntersectsPolygon( Vector *line, Vector *poly, long VertexCount, Vertex &iPoint )
{
float D;
Vector          PolyNormal;

MakeNormal( poly, PolyNormal, D );

if( !LineCrossPlane( line, PolyNormal, D ) )
        return false;

LineIntersectPlane( iPoint, line, PolyNormal, D );

if( !IsPointBounding( iPoint, poly, VertexCount ) )
        return true;
}

· 직선 교차점 알고리즘을 이용한 폴리곤 충돌 검사
위에서 말한 직선 교차점 알고리즘은 직선과 폴리곤의 충돌을 알아보는 방법이었다. 그렇다면 지금부터 이 
알고리즘을 이용해 폴리곤간의 충돌검사 알고리즘을 만들어 보도록 한다. 직선 교차점 알고리즘을 이용해서 
폴리곤 충돌 검사를 하는 것은 매우 단순한 작업이다. 먼저 충돌 검사를 수행할 폴리곤을 A, B라 하고 폴리곤 
A의 각 경계선을 직선으로 정의한다. 그리고 이렇게 정의된 직선들과 폴리곤 B를 직선 교차점 알고리즘을 
이용해서 각각 충돌 검사를 한다. 충돌 검사에서 사용된 직선들은 폴리곤 A의 경계선을 나타내기 때문에 
이들 중 어느 하나라도 폴리곤 B와 교차한다면 두 개의 폴리곤은 서로 교차한다고 말 할 수 있다. 그러나 
어느 한 폴리곤만을 기준으로 처리할 경우 두 개의 폴리곤이 서로 교차함에도 불구하고 교차점을 알아낼 수 
없는 상황이 발생하게 된다. 따라서 A, B 폴리곤의 경계선을 모두 상호 비교해야 한다.

이것을 함수화 하면
bool  PolygonIntersect( Vector *A, long AVertexCount, Vector *B, long BvertexCount, Vector &iPoint )
{
long n;
Vector         Edge[2];

for( n = 0; n < AVertexCount; n++ )
{
  Edge[0] = A[n];
  Edge[1] = A[(n+1) % AVertexCount];
if( LineIntersectsPolygon( Edge, B, BvertexCount, iPoint ) )
        return true;
}

for( n = 0; n < BVertexCount; n++ )
{
  Edge[0] = B[n];
  Edge[1] = B[(n+1) % BVertexCount];
if( LineIntersectsPolygon( Edge, A, AvertexCount, iPoint ) )
        return true;
}

return false;
}

· 복잡한 충돌검사 처리
충돌 검사 알고리즘은 폴리곤에 평행하게 접근하는 직선과 충돌하기 바로 직전에는 평면의 앞쪽에 있다가 
충돌 후에는 폴리곤의 반대편으로 이동하는 경우 충돌을 정확하게 알아내지 못한다. 보통의 경우 이러한 
문제는 따로 처리할 필요가 없다. 왜냐하면, 가상세계의 객체는 복잡한 폴리곤으로 구성되기 때문에 객체를 
구성하는 폴리곤 중 하나라도 충돌이 포착되면 충돌처리를 해주면 되기 때문이다.

그러나 객체의 이동 속도가 굉장히 빠르다거나 혹은 사용자의 컴퓨터가 느릴 경우 객체의 어떤 폴리곤도 
충돌 객체에 걸리지 않고 빠져나가는 경우가 발생하게 된다. 이 문제를 해결하기 위한 가장 간편한 방법은 
첫번째 프레임에서의 객체의 위치와 다음 프레임에서의 객체의 위치를 연결하는 직선을 이용하는 것이다. 
이 직선 중에 하나가 다른 물체와 교차한다면 두 객체가 충돌한 것이기 때문에 물체를 이동시키지 말아야 한다.

· 충돌 검사를 위한 최적화
많은 폴리곤에 대하여 충돌검사를 수행한다면 수많은 수학적 연산에 의해 수행 속도를 떨어뜨릴 위험이 있기 
때문에 충돌검사 최적화를 해주어 충돌검사 회수를 감소시킬 수 있다.
첫번째 최적화 방법은 각 폴리곤과 객체에 대한 바운딩 스피어를 이용하는 방벙이다. 움직이는 객체의 바운딩 
스피어를 가상세계에 존재하는 다른 객체의 바운딩 스피어와 먼저 검사해서 서로 충돌이 발생했다면 각 객체가 
갖는 폴리곤의 바운딩 스피어와 다시 비교해서 충돌이 발생한 폴리곤을 찾는다. 마지막으로 이렇게 찾은 
폴리곤에 대해서 다시 라인 교차점 알고리즘을 이요해 정확한 교차점을 계산한다. 이방법은 바운딩 스피어를 
이용해 비교하는 범위를 줄여 가는 최적화 기법이라 할 수 있다. 따라서 이 방법을 사용할 경우 객체가 갖는 
모든 폴리곤에 대해 라인 교차점 알고리즘을 적용해야 하는 비효율을 줄일 수 있게 된다. 두개의 바운딩 
스피어가 교차하는지를 알아보기 위해서는 두 바운딩 스피어의 중심간의 거리를 계산하면 된다. 만일 이 
거리가 두 바운딩 스피어의 반지름의 합보다 작다면 두개의 구는 서로 교차하고 있다.

¨NOTE
폴리곤 또는 객체에 대한 바운딩 스피어를 계산하기 위해서는 먼저 바운딩 스피어의 중심점으로 사용되는 
객체의 중심점을 계산해야 한다. 모든 버텍스의 x, y, z값을 각각 더한 값을 버텍스의 개수로 나누면 객체의 
중심점을 구할 수 있으며, 이 중심으로부터 가장 먼 거리에 있는 버텍스와 거리가 바로 바운딩 스피어의 
반지름이 된다.

두 번째 최적화 방법은 가상세계를 작은 단위로 분할하는 방법이다. 이렇게 분할된 공간 안에 속하는 폴리곤을 
리스트로 관리하면서 충돌 검사를 할 때는 이 영역에 포함되는 폴리곤에 대해서만 충돌 검사를 수행하는 방법이다. 
이 방법을 사용하게 되면 특정 객체가 존재하는 가상세계의 일부 영역에 대해서만 충돌 검사를 할 수 있기 때문에 
충돌 검사를 해야하는 횟수를 크게 줄일 수 있는 장점이 있다. 하지만 복잡한 자료구조를 구성해야 한다는 단점이 있다.

· 바운딩 박스
바운딩 박스를 만들 때 우선 모든 면의 노말이 밖을 바라보게 바운딩 박스를 만들던지 아니면 모든면이 안으로 보는 
바운딩 박스를 만들어야 한다. 그럼 평면에 방정식을 이용해서 바운딩박스의 각각의 면에 한점이 면의 앞에 있는지
뒤에 있는지를 알수가 있을 것 이다. 만약 모든 바운딩박스의 면을 밖으로 바라보게 하였다면 한점이 바운딩박스의 
모든면에 대해서 평면의방정식 < 0 을 성립한다면 그 한점은
바운딩 박스에 안에 들어 있는 것임을 알수 있다.

· 바운딩 충돌
바운딩 박스는 8점으로 이루워져 있으므로 바운딩박스 & 바운딩박스의 충돌은 한쪽의 바운딩 박스에 다른쪽의 
바운딩박스의 8점을 검색해서 충돌여부를 판단하구 충돌이 없다면 이번에는 반대로 한번 더 검사해서 최종 결정을 
내리면 된다.

· 몇가지 주의점
바운딩박스가 만약 움직이는 물체에 붙어있다면 그 물체가 움직이고 회전하는 값을 그대루 바운딩 박스에 적용해야 
합니다. 바운딩 박스가 회전을 하든지 움직일때 마다 항상 새로 바운딩박스의 면의 노말을 구해서 평면의 방정식에 
사용해야 한다. 그래야 정확한 충돌을 찾을 수 있다.


[원본] http://expert3d.egloos.com/598593
Posted by 시그v
이전버튼 1 이전버튼

블로그 이미지
Computer graphics & Programming
시그v

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.11
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

최근에 올라온 글

최근에 받은 트랙백

최근에 달린 댓글

믹시