http://kimdw819.tistory.com/41




조명 효과는 vertex 마다 normal 과 비츼 방향을 내적해 구합니다.  범프 매핑에서는 픽셀마다 normal을 줘서 빛에 대한 효과를 줘서 표면에 울퉁불퉁하거나, 주름지고, 물결치는 것 같이 표면이 불균일하게 보이도록 합니다.  
픽셀마다 normal을 얻어 오기 위해 텍스쳐 맵을 하나 더 사용합니다. 이 텍스쳐에 높이 값을 넣어서, 인접 픽셀간의 높이 차이를 통해 normal을 구할 수 있습니다.

[그림1]


위의 그림1은 높이 맵으로 만들어 놓은 것을 읽어서 법선 맵으로 만드는 소스의 부분입니다. 
우선 텍스쳐를 높이 맵과 동일한 크기로 만들고, 텍스처에 읽은 후에 법선을 만들어 놓습니다. D3DXComputeNormalMap에서 뒤에 D3XCHANNEL_RED 부분은 높이 맵에서 ARGB에서 R 값을 읽어서 10을 scale로 높이를 만들어 비교후에 법선을 만든다는 뜻입니다.  R * 10 이 해당 픽셀의 높이. 근처 픽셀의 높이 값을 구해 법선으 구한다.

 월드 좌표계에 있는 빛과 픽셀에 있는 normal을 내적을 통해 계산을 하려면 같은 공간에 일치 시켜야 합니다. 2개의 공간에 있으니 둘 중에 하나를 변환시켜 같은 공간에 있으면 되는데, 월드 좌표계에 있는 빛을 픽셀이 위치한 좌표계로 옮기는게 좀 더 간단합니다. 픽셀이 위치한 좌표계는 여러 책을 보니 다양하게 정의를 하는데 vertex 공간 좌표계, tangent 공간 , 텍스쳐 공간 좌표계 라는 정의가 있습니다.  
이 공간은 법선은 항상 양의 z축을 가리키는 좌표계입니다.  tangent 공간이라고 정의하고 설명을 하겠습니다.
tangent 공간의 법선과 월드 공간 좌표계에 위치한 빛을 내적하려면, 위에 말했다시피 월드 공간 좌표계의 빛을 tangent 공간으로 변환을 해야 합니다.  object space -> tangent space로 변환 하는 행렬을 찾도록 하겠습니다.

 tangent 공간의 2가지 접선 벡터 T,B는 텍스쳐 맵에 정렬된 접선 벡터입니다.( 여기서 접선 벡터는 2개의 벡터가 붙어있다는 것에 중점을 두고 있습니다. 이 2개의 벡터를 외적하면 2개의 벡터와 직교하는 벡터가 만들어 집니다)

T는 텍스쳐 가로 좌표 u 에 따른 위치 변화 , B는 텍스쳐 세로 좌표 v에 따른 위치 변화로 구할 수 있습니다


밑에 그림에서 P, P1 ,P2 는 정점(vertex)의 위치를 나타냅니다. Q1,Q2는 이를 연결하는 벡터르 의미합니다.
밑에 위치한 식들은 T,B를 구하는 방법을 나타냅니다.




앞에 상수항은 후에  T,B 벡터들을 재 정규화 할때 normalize 가 되서 없어지기 때문에 생략 가능합니다.


참고 도서.  Real-Time rendering -  Tomas Eric 공저
                DirectX 9 셰이더 프로그래밍   - 타카시 이마가레 저 
               3D 게임 프로그래밍 & 컴퓨터 그래픽을 위한 수학  - Eric Lengyel  저
               Direct3D ShaderX          - wolfgang F. engel 공저


반응형
1. 호도법, 평면각(라디안,rd), 입체각(스테라디안,sr) 이란?

  ※ 호도법 (Circular Measure)
     - 일반 각도법으로는, 무리수 표현이 어려워지는 등, 여러가지로 불편하여, 
     - 길이 비율에 따라 각도를 표현하는 방법

  ㅇ 평면각 (Plane Angle) (라디안)
     - 두 `길이`의 비율로 표현되는 각도

  ㅇ 입체각 (Solid Angle) (스테라디안) 
     - `넓이`와 `길이의 제곱`과의 비율로 표현되는 각도


2. 평면각(라디안) 및 입체각(스테라디안)의 단위 [ SI 단위계의 보조단위 ]

  ※ 평면각,입체각은 순수한 수(數)로 된 단위로써 사실상 무 차원의 물리량이나,
     - 호도법 표기를 강조하기 위해 단위를 각각 radian, steradian 으로 씀

  ㅇ radian     :  평면각의 단위 [rad]
     - 1 radian 은, 
         . 반지름과 동일한 호의 길이가 품는 각도
            .. 즉, 원주 상에서 그 반경과 같은 길이의 호를 끊어서 얻어진,
            .. 2개의 반경 선 사이에 낀 (평면의) 각을 말함

     - 반지름 r인 원에서, 호의 길이 s인 평면각은,  α = s / r = 원주 길이 / 반지름 길이

     - 일반 각도 및 라디안의 관계는,
        . 완전한 원은 원주 길이가 2πr 이므로, 2πr/r [rad]= 2 π [rad] = 360 [˚]

     - 例) 원 전체 원주(1원주) =>  2π[rad] = 360[˚] => 1 [rad] = 180/π[˚] ≒ 57.2958[˚]

  ㅇ steradian  :  입체각의 단위 [sr]
     - 1 steradian 은, 
         . 단위 구에서, 구면 상의 단위 면적을 품는 입체 각도
         . 또는, 구의 반경의 제곱과 같은 표면적에 해당하는 공간 입체 각도
            .. 구의 중심을 정점으로한 구표면에서 그 구의 반경을 한 변으로 하는,
            .. 정사각형 면적(r²)과 같은 곡면 표면적(r²)을 갖는 공간적인 각을 말함

     - 반지름 r인 구에서, 표면적 A에 해당하는 입체각은,  ω = A / r2

     - 例) 전 구의 입체각은,
        . 구의 전 표면적이 4πr²이므로,  4π [sr]


3. 구의 미소 면적 및 미소 입체각(differential solid angle) (구좌표계에서)

  ㅇ 구 전체 표면적  A   = 4πr2   [㎡] 
  ㅇ 미소 면적소     dA  = r2 sinθdθdΦ  [㎡]
  ㅇ 미소 입체각     dΩ = sinθdθdΦ  [sr]
 
 

 

 

 

입체각

[ solid angle , 立體角 ]
 
요약
공간에서 O를 한 끝점으로 하는 사선(射線:ray) OA가 O의 둘레를 회전하여 처음의 위치로 되돌아올 때, 그려진 도형을 입체각이라 하며 O를 꼭지점이라 한다. 입체각의 단위는 1sr(스테라디안:steradian)이라고 한다.

공간에서 O를 한 끝점으로 하는 사선(射線:ray) OA가 O의 둘레를 회전하여 처음의 위치로 되돌아올 때, 그려진 도형을 입체각이라 하며 O를 꼭짓점이라 한다. 이 경우 입체각의 크기는 O를 중심으로 하여 반지름 1인 구(球)가 이 입체각의 변과 만나서 이루어지며, 구면 위의 도형 S의 넓이로 측정된다.

S의 넓이가 1일 때, 이 입체각을 1sr(스테라디안:steradian)이라고 한다. 이것이 입체각의 단위이며, 평면의 경우의rad(라디안:radian)의 정의를 구면 위로 확장한 것이라고 할 수 있다. 또 전 구면의 중심점에 대한 입체각의 1/(4π)이 1sr이 된다.

[네이버 지식백과] 입체각 [solid angle, 立體角] (두산백과)

 

 

 


 



출처 : 위키

스테라디안

위키백과, 우리 모두의 백과사전.
 
 
1 스테라디안.

스테라디안(steradian, 기호 sr)은 입체각의 국제 단위이다. 평면에서 각도를 나타내는 라디안처럼, 3차원 공간에서 각도로 나타나는 2차원의 영역을 나타낼때 사용된다. 용어는 견고함을 의미하는 그리스어: στερεός 

스테레오스[*]

 와 광선을 의미하는 라틴어: radius 

라디우스

에서 유래한다.

스테라디안은 라디안 처럼 무차원상수이다. 즉, 1 sr = m2·m-2 = 1. 구 전체의 입체각은 4\pi sr이 된다.

정의[편집]

1 스테라디안은 반지름이 r인 의 표면에서 r2인 면적에 해당하는 입체각이다.

콘의 단면(1) 과 잘린 반구(spherical cap) (2)

면적 A가 r2 와 같고, 반구의 표면적 (

A = 2\pi rh

)과도 같다면, 

\frac{h}{r}=\frac{1}{2\pi}

 이 관계를 따르게 된다. 그러므로, 각 

\theta

를 갖는 콘의 입체각은 다음과 같아진다.

 \begin{align} \theta & = \arccos \left( \frac{r-h}{r} \right)\\        & = \arccos \left( 1 - \frac{h}{r} \right)\\        & = \arccos \left( 1 - \frac{1}{2\pi} \right) \approx 0.572 \,\text{rad} \mbox{ or } 32.77^\circ \end{align}

이것은 축으로부터의 각(apex angle)의 2

\theta

 ≈ 1.144 rad or 65.54°에 대응된다.

구의 면적이 

4\pi

 r2이므로, 정의에 의해 구는 4

\pi

 =12.56637 스테라디안이다. 같는 논리로, 최대 입체각은 4

\pi

sr 이다. 스테라디안은 squared radian으로 불리기도한다.

스테라디안은 1 라디안의 angle excess(angle excess)를 갖는 다각형의 구면, 또는 완전한 구의 1/(4

\pi

), 또는 (180/

\pi

)2 제곱 각(square degree), 또는 3282.80635 제곱 각과 같다.

스테라디안은 SI 보조 단위였으나, 1995년 SI 보조 단위가 폐지 되면서, SI 유도 단위가 되었다.

라디안과 유사성[편집]

2차원 평면에서, 라디안으로 표현되는 각은 잘려지는 호의 길이와 관련이 있다.

\theta = \frac{l}{r} \,
l 은 호의 길이
r 은 원의 반지름.

3차원에서, 스테라디안으로 표현되는 입체각은 잘려지는 면적과 관련이 있다.

\Omega = \frac{S}{r^2} \,
S 은 표면적
r 은 구의 반지름.

 

 

 

 

 


 

 

http://goo.gl/QgJXjg

 

1스테라디안(1sr)은 몇도인가요??

 

 

호도법의 360도는 라디안으로 고치면 2∏ 라디안이 됩니다.

따라서 1라디안 = 360/2∏ 이고 ∏는 3.14정도되므로 1라디안 = 57.32도 정도가 됩니다.

 

스테라디안은 원의 각도를 나타내는 단위가 아니고 구(공)의 각도를 나타내는 단위입니다.

즉, 구 반지름의 제곱과 같은 구 표면적에 대응하는 구의 입체각으로 정의됩니다.

예를들어 공의 반지름이 1m 라고 하면 반지름의 제곱은 1m^2 가 됩니다.

공의 표면상에 한면이 1m인 정사각형을 그리고 이 정사각형의 각 꼭지점에서 공의 중심까지

선을 그린다고 하면 4개의 선이 공의 중심에서 만나게 됩니다. 이때 이 4개의 선이 만든

입체각을 나타내는 단위가 스테라디안입니다.

구의 표면적은 잘 알려져있다시피 4∏r^2 입니다. 여기서 r은 구의 반지름이지요.

따라서 구의 중심각은 4∏가 됩니다. (원의 중심각은 360도 인것처럼)

 

 


 

 

Solid Angle (입체각) 


복사http://blog.naver.com/hsg556/110130248554

 

 

 

기본 개념

 

평면각이 평면에서의 퍼짐 정도를 나타내는 척도의 개념이라면 입체각은 말 그대로 공간에서의 퍼짐 정도를 나타내는 척도로서의 각도이다. 이러한 공간에서의 퍼짐을 표시하는 각도의 개념은 등방위성으로 퍼지는 현상, 예를 들어 점원(point source)으로부터의 빛, 전파, 방사선이 발산될때의 세기를 기술하는 데 아주 유용한 도구가 된다.

아래 그림에서 점0에 점광원이 놓여져 있을 때, 비록 면‘가’와‘가`’는 서로 면적의 형상 및 크기가 다르지만 두 면 다 빛의 동일한 퍼짐 각도 이내에 있기에 이 두 면에 도달하는 빛 에너지의 합은 같게 된다. 이와같이 이러한 의미로 정의되는 각도를 공간에서 정의하면 공간에서의 세기와 같은 문제를 쉽게 다룰수 있는 도구가 된다.

(그림1 입체각의 개념 및 투영)

입체각의 정성적 및 정량적 정의

 

이러한 기능의 개념인 입체각의 정의는?

이의 정성적 정의는 위에서 설명한 그대로 공간에서의 퍼짐을 표시하는 척도.

 

실제사용을위해서는?

정량적 정의가 필요

 

정량적 정의의 이해를 위해서 먼저 위의 정성적 정의의 입체각 성격을 보면,

이 각도는 점원으로부터 공간의 어느 지점까지의 거리에 반비례하고,

좀더 정확히 이야기하면 거리의 제곱에 반비례하고, 또 그 지점에서 얼마나 펼쳐지는 가에 비례함을 알 수 있다.

 

즉 이러한 특성에 따라 입체각의 크기를 다음 식과 같이 정량적으로 정의하면 필요한 특성을 지니게 된다.

 

   ......      1)

 

그런데 이 식에서 면적은 점원에 기울어진 면적이 아니라 점원으로부터 그 면까지 그려지는 선분에 수직한 면적이어야만 위에서 이야기된 입체각에 요구된 특성이 만족될 수 있다. 즉,

 

   

     ........   2)

 

이를 vector 연산식으로 표시하면 표현은 매우 간단해지는 데,  미소면적 dA에 대한  입체각 dΩ는,

........   3)

n 은 해당 면의 단위 표면 vector, dA는 수직성분 여부와 관련없이 그 면의 실제크기, r은 점원에서 해당 면까지의 거리 vector

 

식2) 또는 대등하게 식3)이 고체각에 대한 정량적 정의다. 참고로, 3)에서 분모의 r의 지수가 2가 아닌 3이지만 분자에 r이 있기에 결국은 앞에서 식2) 와 같이 r의 지수는 2가 된다.

 

정의식 식3) 을 이용하여 미소면적이 아닌 유한 면적에 대해 적용하고 , 식3) 의 vector연산을 간략히 하기 위하여 적용면적에 투영 면적의 개념을 적용한다. 달리 표현하면 그 면의 거리 vector r 에 대해 수직하면서 동일한 고체각도인 면에 대해 적용한다.

즉 그림 1)의 면  '나'는,  면 '가'를 동일 원점을 중심으로 하는 구에 투영시킨 면이고 이 두 면은 공간에서 퍼짐의 정도가 서로 같기에  고체각 역시 서로 같다. 따라서 면 '나'에 대한 고체각 작업 결과는 '가'에 대한 결과와 동일. (단, 그림에서는 두 면의 개념상의 차이를 나타내기 위해 면 '가'와 '나'의 r 이 서로 다르게 그려져있는 데, 이해를 쉽게 하기 위해  면 '나'를 당겨와 r 이 면 '가'의 r 이 되는 지점에 면 '나'가 위치한 것으로 상정한다.)

 

면 '나'에서

 ....... 4)

식 4) 를 식 2) 또는 식 3)에 대입하면,

..........  5)

 

식 5)  로부터 알 수 있듯이 입체각은 무차원인데 편의상 steradian 단위 또는 sr이란 약자를 붙여서 표시하기도 한다.

또한 고체각이라는 용어가 사용되기도 한데 이는 입체각에 해당되는 영어표현 solid angle을 그대로 옮긴 것으로 바람직스럽지 못한 용어다.

 

반구에 대한 고체각은 식 5) 로 부터

 ....... 6)

또는 식 2)로부터도 역시 같은 결과를 얻을 수 있다.

 

즉, 구의 표면적 = 

 이기에

.............................     7)

 

유사하게,

 

입체각의 적용

 

고체각의 개념은 이러하고, 좀 현실적인 감을 익히기 위해 다음 그림과 같이

(그림 2. 원추의 입체각)

 

사이각이 θ 인 직삼각형 1-2-3을 한바퀴 회전시켰을 때 만들어지는 원추의  입체각은

........... 8)

 

식 8) 에 의해 몇가지 사이각θ에 대한 입체각은 다음과 같다

 평면각 θ(도)  입체각sr
 0  0
 10  0.0954
 20  0.379
 30  0.842
 40  1.470
 45  1.840
 60  3.142
 90  6.283 (= 
:반구)
 180  12.6 (=
:공간전체)

 

===========================================================================================================================

[출처] : 네이버 오픈사전

네이버 스마트 에디터 수식입력기로 수식 입력... 좋은데?

띄어쓰기 및 줄 맞춤, 단어 맞춤 추가

[출처] Solid Angle (입체각)|작성자 GUNN

 

ref : http://www.ktword.co.kr/test/view/view.php?id=545&m_temp1=975&nav=1

 

 

반응형

BLOG main image




투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(norm) 을 구하는 방법


D3DXVECTOR3 p1InCamera(33,-24,-2),p2InCamera(0,0,2);


D3DXVECTOR3 normC,norm(10,0,-10),normP;

D3DXVECTOR4 outP1InProj,outP1InProj_w,outP2InProj,outP2InProj_w,outNorm,resultCameraP;

D3DXVec3Normalize( &norm,&norm );

D3DXVECTOR3 outP1InProj_( outP1InProj.x,outP1InProj.y,outP1InProj.z ),cameraP,projP;


/*

* D3DXVec3Transform 는 w 로 결과 벡터를 나누지 않고 그대로 D3DXVECTOR4 를 만들지만

* D3DXVec3TransformCoord 는 결과 벡터를 w 로 나누고 w 값을 1 로 채우는 형식이다

* D3DXVec3TransformCoord 로써 투영행럴의 역행렬 을 적용하여 카메라공간에서의 를 점을 얻을 수 있다

**/


D3DXVec3Transform( &outP1InProj, &p1InCamera, &matProjection );

projP.x=outP1InProj.x;

projP.y=outP1InProj.y;

projP.z=outP1InProj.z;

projP/=outP1InProj.w;

D3DXVec3Transform( &resultCameraP,  &projP, &invProj );

resultCameraP/=resultCameraP.w;



필요한경우 노멀벡터또한 투영행렬로 변환했다가 역투영행렬로 변환 할 수 있는데 다음과 같다


D3DXVec3TransformCoord( &normP, &norm, &matProjection );        //normP 는 내부적트로 계산된 normPInner/=normPInner.w 의 형태임을 주의해야한다!!!

D3DXVec3TransformCoord( &normP,  &normP, &invProj );             // 아래또한 같다


D3DXVec3TransformNormal 은 투영행렬의 특성상 z 값이 소실될 수 있음으로 z 값이 제대로 나오지 못한다



한가지 주의사항은 normal 을 투영행렬 공간에서 어떠한 연산을 하면 안된다는 것이다

왜냐하면 D3DXVec3TransformCoord 를 적용한 투영공간상의 z 는  0 < z  < 1  의 값으로 그려지는 오브젝트에 대해서 z값이 

항상 양수임으로



아래는 d3dx9math.h 내용

언젠가 올려놓은적이 있는것 같은데 긁어옴








 

http://cafe.naver.com/jzsdn/6192

 

 

Matematika v Direct3DX

(English info)

V této kapitole je seznam matematických funkcí Direct3DX (což je jakýsi toolkit pro Direct3D (DirectXGraphics), jistá obdoba knihovny GLUT pro OpenGL), spolu s popisem matematiky, kterou tyto funkce provádí.

Upozorňuji, že toto není oficiální dokumentace k Direct3DX (ta se nalézá někde na MSDN).

Vektory

D3DXVecNAdd(Out, V1, V2)

Out = V1 + V2

D3DXVecNBaryCentric(Out, V1, V2, V3, f, g)

Out = V1 + f⋅(V2V1) + g⋅(V3V1)

D3DXVecNCatmullRom(Out, V1, V2, V3, V4, s)

/0100\/V1\
||||
|−0,500,50||V2|
Out = (1,s,s²,s³)||||
|1−2,52−0,5||V3|
||||
\−0,51,5−1,50,5/\V4/

D3DXVecNDot(U, V)

Result = U ⋅ V = ∑iUiVi

D3DXVecNHermite(Out, V1, T1, V2, T2, s)

/1000\/V1\
||||
|0010||V2|
Out = (1,s,s²s³)||||
|−33−2−1||T1|
||||
\2−211/\T2/

D3DXVecNLength(V)

Result = |V| = √(∑iVi²)

D3DXVecNLengthSq(V)

Result = |V|² = ∑iVi²

D3DXVecNLerp(Out, V1, V2, s)

Out = V1 + s(V2V1);

D3DXVecNMaximize(Out, U, V)

Outi = max(Ui, Vi)

D3DXVecNMinimize(Out, U, V)

Outi = min(Ui, Vi)

D3DXVecNNormalize(Out, V)

Out = 1|V|V

D3DXVecNScale(Out, V, s)

Out = sV

D3DXVecNSubtract(Out, V1, V2)

Out = V1 − V2


2D Vektory

D3DXVec2CCW(U, V)

Result = UxVy − UyVx

D3DXVec2Transform(Out, V, M)

a = (Vx, Vy, 0, 1)
b = (a×M)T
Out = (bx, by)

D3DXVec2TransformCoord(Out, V, M)

a = (Vx, Vy, 0, 1)
b = (a×M)T
Out = 1bw(bx, by)

D3DXVec2TransformNormal(Out, V, M)

a = (Vx, Vy, 0, 0)
b = (a×M)T
Out = (bx, by)


3D Vektory

D3DXVec3Cross(Out, V1, V2)

Out = V1 × V2

D3DXVec3Project(Out, V, Viewport, Proj, View, World)

a = (Vx, Vy, Vz, 1)
b = a×World×View×Proj
c = bbw
Outx = ViewportX + ViewportWidth*(1+cx)/2
Outy = ViewportY + ViewportHeight*(1-cy)/2
Outz = ViewportMinZ + cz*(ViewportMaxZ-ViewportMinZ)

D3DXVec3Transform(Out, V, M)

a = (Vx, Vy, Vz, 1)
b = (a×M)T
Out = (bx, by, bz)

D3DXVec3TransformCoord(Out, V, M)

a = (Vx, Vy, Vz, 1)
b = (a×M)T
Out = 1bw(bx, by, bz)

D3DXVec3TransformNormal(Out, V, M)

a = (Vx, Vy, Vz, 0)
b = (a×M)T
Out = (bx, by, bz)

D3DXVec3Unproject(Out, V, Viewport, Proj, View, World)

M = (World×View×Proj)−1
ax = (Vx-Viewportx)*2/ViewportWidth - 1
ay = 1 - (Vy-Viewporty)*2/ViewportHeight
az = (Vz-ViewportMinZ)/(ViewportMaxZ-ViewportMinZ)
aw = 1
b = (a×M)T
Out = 1bw(bx, by, bz)


4D Vektory

D3DXVec4Cross(Out, U, V, W)

a = VxWy − VyWx
b = VxWz − VzWx
c = VxWw − VwWx
d = VyWz − VzWy
e = VyWw − VwWy
f = VzWw − VwWz
Outx = fUy − eUz + dUw
Outy = fUx + cUz − bUw
Outz = eUx − cUy + aUw
Outw = dUx + bUy − aUz

D3DXVec4Transform(Out, V, M)

Out = (V×M)T


Matice (4×4)

D3DXMatrixAffineTransformation(Out, s, c, r, t)

/s(2(ry² + rz²) − 1)2s(rxry + rzrw)2s(rxrz − ryrw)0\
||
|2s(rxry − rzrw)s(2(rx² + rz²) − 1)2s(ryrz + rxrw)0|
Out =||
|2s(rxrz + ryrw)2s(ryrz − rxrw)s(2(rx² + ry²) − 1)0|
||
\tx+2(cxry²+cxrz²−cyrxry+cyrzrw−czrxrz−czryrw)ty+2(cyrx²+cyrz²−cxrxry−cxrzrw−czryrz+czrxrw)tz+2(czrx²+czry²−cxrxrz+cxryrw−cyryrz−cyrxrw)1/

D3DXMatrixfDeterminant(M)

Result = det M

D3DXMatrixIdentity(Out)

/1000\
||
|0100|
Out = E =||
|0010|
||
\0001/

D3DXMatrixInverse(Out, D, M)

D = det M
Out = M−1

D3DXMatrixIsIdentity(M)

Result = ME

D3DXMatrixLookAtRH(Out, Eye, At, Up)

v = Normalized(EyeAt)
l = Up×v
u = v×l

/lxuxvx0\
||
|lyuyvy0|
Out =||
|lzuzvz0|
||
\−l⋅Eye−u⋅Eye−v⋅Eye1/

D3DXMatrixLookAtLH(Out, Eye, At, Up)

v = Normalized(AtEye)
r = Up×v
u = v×r

/rxuxvx0\
||
|ryuyvy0|
Out =||
|rzuzvz0|
||
\−r⋅Eye−u⋅Eye−v⋅Eye1/

D3DXMatrixMultiply(Out, M1, M2)

Out = M1×M2

D3DXMatrixOrthoRH(Out, w, h, n, f)

Q = (f−n)−1

/2w000\
||
|02h00|
Out =||
|00−Q0|
||
\00−Qn1/

D3DXMatrixOrthoLH(Out, w, h, n, f)

Q = (f−n)−1

/2w000\
||
|02h00|
Out =||
|00Q0|
||
\00−Qn1/

D3DXMatrixOrthoOffCenterRH(Out, l, r, t, b, n, f)

Q = (f−n)−1
w = r−l
h = b−t

/2w000\
||
|02h00|
Out =||
|00−Q0|
||
\−(r+l)2−(t+b)2−Qn1/

D3DXMatrixOrthoOffCenterLH(Out, l, r, t, b, n, f)

Q = (f−n)−1
w = r−l
h = b−t

/2w000\
||
|02h00|
Out =||
|00Q0|
||
\−(r+l)2−(t+b)2−Qn1/

D3DXMatrixPerspectiveRH(Out, w, h, n, f)

Q = (f−n)−1

/2w000\
||
|02h00|
Out =||
|00−Qf−1|
||
\00−Qnf0/

D3DXMatrixPerspectiveLH(Out, w, h, n, f)

Q = (f−n)−1

/2w000\
||
|02h00|
Out =||
|00Qf1|
||
\00−Qnf0/

D3DXMatrixPerspectiveFovRH(Out, Fovy, Aspect, n, f)

Q = (f−n)−1
y = cotg(Fovy)2

/yAspect000\
||
|0y00|
Out =||
|00−Qf−1|
||
\00−Qnf0/

D3DXMatrixPerspectiveFovLH(Out, Fovy, Aspect, n, f)

Q = (f−n)−1
y = cotg(Fovy)2

/yAspect000\
||
|0y00|
Out =||
|00Qf1|
||
\00−Qnf0/

D3DXMatrixPerspectiveOffCenterRH(Out, l, r, t, b, n, f)

Q = (f−n)−1
w = r−l
h = b−t

/2w000\
||
|02h00|
Out =||
|(r+l)w(t+b)h−Qf−1|
||
\00−Qnf0/

D3DXMatrixPerspectiveOffCenterLH(Out, l, r, t, b, n, f)

Q = (f−n)−1
w = r−l
h = b−t

/2w000\
||
|02h00|
Out =||
|−(r+l)w−(t+b)hQf1|
||
\00−Qnf0/

D3DXMatrixReflect(Out, Plane)

(a,b,c,d) = (Planea,Planeb,Planec,Planed)√(Planea²+Planeb²+Planec²)

/1−2a²−2ba−2ca0\
||
|−2ab1−2b²−2cb0|
Out =||
|−2ac−2bc1−2c²0|
||
\−2ad−2bd−2cd1/

D3DXMatrixRotationAxis(Out, V, Angle)

s = sin Angle
c = cos Angle
d = 1−c
(x,y,z) = V

/dx²+cdxy+zsdxz−ys0\
||
|dxy−zsdy²+cdyz+xs0|
Out =||
|dxy+ysdyz−xsdz²+c0|
||
\0001/

D3DXMatrixRotationQuaternion(Out, Q)

(x,y,z,w) = Q

/1−2y²−2z²2xy+2zw2xz−2yw0\
||
|2xy−2zw1−2x²−2z²2yz+2xw0|
Out =||
|2xz+2yw2yz−2xw1−2x²−2y²0|
||
\0001/

D3DXMatrixRotationX(Out, Angle)

s = sin Angle
c = cos Angle

/1000\
||
|0cs0|
Out =||
|0−sc0|
||
\0001/

D3DXMatrixRotationY(Out, Angle)

s = sin Angle
c = cos Angle

/c0−s0\
||
|0100|
Out =||
|s0c0|
||
\0001/

D3DXMatrixRotationYawPitchRoll(Out, Yaw, Pitch, Roll)

(sa,sb,sc) = sin (Roll, Pitch, Yaw)
(ca,cb,cc) = cos (Roll, Pitch, Yaw)

/ca⋅cc+sa⋅sb⋅sc−sa⋅cc+ca⋅sb⋅sccb⋅sc0\
||
|sa⋅cbca⋅cb−sb0|
Out =||
|−ca⋅sc+sa⋅sb⋅ccsa⋅sc+ca⋅sb⋅cccb⋅cc0|
||
\0001/

D3DXMatrixRotationZ(Out, Angle)

s = sin Angle
c = cos Angle

/cs00\
||
|−sc00|
Out =||
|0010|
||
\0001/

D3DXMatrixScaling(Out, x, y, z)

/x000\
||
|0y00|
Out =||
|00z0|
||
\0001/

D3DXMatrixShadow(Out, Light, Plane)

(a,b,c,d) = (Planea,Planeb,Planec,Planed)√(Planea²+Planeb²+Planec²)
(x,y,z,w) = Light
f = Lightx⋅Planea + Lighty⋅Planeb + Lightz⋅Planec + Lightw⋅Planed

/f−xa−ya−za−wa\
||
|−xbf−yb−zb−wb|
Out =||
|−xc−ycf−zc−wc|
||
\−xd−yd−zdf−wd/

D3DXMatrixTransformation(Out, Scenter, Srot, Scaling, Rotcenter, Rot, Trans)

D3DXMatrixTranslation(A, -Scenterx, -Scentery, -Scenterz)
D3DXMatrixScaling(B, Scalingx, Scalingy, Scalingz)
D3DXMatrixRotationQuaternion(CSrot)
u = Scenter − Rotcenter
D3DXMatrixTranslation(D, ux, uy, uz)
D3DXMatrixRotationQuaternion(E, Rot)
v = Rotcenter + Trans
D3DXMatrixTranslation(F, vx, vy, vz)
Out = A×CT×B×C×D×E×F

D3DXMatrixTranslation(Out, x, y, z)

/1000\
||
|0100|
Out =||
|0010|
||
\xyz1/

D3DXMatrixTranspose(Out, M)

Out = MT


Roviny

D3DXPlaneDot(P, V)

Result = (Pa, Pb, Pc, Pd)⋅V

D3DXPlaneDotCoord(P, V)

Result = (Pa, Pb, Pc)⋅V + Pd

D3DXPlaneDotNormal(P, V)

Result = (Pa, Pb, Pc)⋅V

D3DXPlaneIntersectLine(Out, P, U, V)

n = (Planea, Planeb, Planec)
d = V − U
Out = U − d(Pd + nU)(dn) [iff dn ≠ 0]

D3DXPlaneFromPointNormal(Out, P, N)

Planea = Nx
Planeb = Ny
Planec = Nz
Planed = −NP

D3DXPlaneNormalize(Out, P)

q = 1√(Pa² + Pb² + Pc²)
Outa = q⋅Pa
Outb = q⋅Pb
Outc = q⋅Pc
Outd = q⋅Pd

D3DXPlaneFromPoints(Out, A, B, C)

v = (B − A) × (C − A)
n = 1|v| v
Outa = nx
Outb = ny
Outc = nz
Outd = −nA

D3DXPlaneTransform(Out, P, M)

Q = P|P|
u = (Qa, Qb, Qc, 0)
D = Qd
A = (−Dux, −Duy, −Duz, 1)
B = A×M
v = u×M
q = 1|v|
Outa = qvx
Outb = qvy
Outc = qvz
Outd = −qvB


반응형



점,벡터 구분의 미묘함을 간략하게 정리한다면


벡터 : 특정 점에서 특정 점까지 상대적 차이 값, 이를 때에따라 단위벡터로 나타낼 수도 있다


점    : 원점에서 시작해 어떤 특정 점까지의 위치



p.s ;;;; 너무 간단한가, 복잡하게 말할것도 없음으로.. ㅎ



BLOG main image

반응형



쿼터니온을 사용해 오브젝트 회전시키기  번역 / Programming 

2006/03/10 14:55

복사http://blog.naver.com/lifeisforu/80022505113


원문 : http://www.gamasutra.com/features/19980703/quaternions_01.htm

 

작년은 하드웨어 가속의 시대로서의 역사에 남겨졌다고 할 수 있다. 폴리곤을 래스터라이즈화하고 텍스처 매핑을 하는 많은 작업들이 전용 하드웨어에 넘겨졌다. 결과적으로 우리 게임 개발자들은 이제 물리적 시뮬레이션 및 다른 기능들을 위해 많은 CPU 사이클을 절약할 수 있게 되었다. 쿼터니온 덕택에 그러한 부가적인 사이클들이 회전 및 애니메이션을 부드럽게 하는 것과 같은 작업을 위해 적용될 수 있게 되었다.

 

많은 게임 프로그래머들은 이미 쿼터니온의 대단한 세계에 대해서 발견했으며, 그것들을 광범위하게 사용하기 시작했다. TOMB RADIDER 타이틀을 포함한 일부 삼인칭 게임들은 그것의 카메라 움직임을 애니메이션하기 위해서 쿼터니온 회전을 사용한다. 모든 삼인칭 게임은 플레이어의 캐릭터의 측면이나 뒤에서 따라가는 가상 카메라를 가지고 있다. 이 카메라는 캐릭터와는 다른 동작을 통해(즉 다른 길이의 호(arc)를 통해서) 움직이기 때문에, 카메라 동작은 (플레이어의) 행동을 따라가기에는 부자연스럽고 플레이어에 비해 너무 "변덕"스러웠다. 이것이 쿼터니온이 해결사로 나서게 된 하나의 영역이다.

쿼터니온의 또다른 일반적 용도는 군용/상업용 비행 시뮬레이터이다. 세 개의 각(roll, pitch, yaw)를 사용하여 비행기의 방향을 조작하는 대신에 상대적으로 x, y, z 축에 대한 회전을 표현하면, 단일 쿼터니온을 사용하는 데 있어 매우 간단해 진다.

올해에 나오는 많은 게임들이 실제적인 물리를 표현하게 될 것이며, 놀라운 게임 플레이와 집중을 가능하게 할 것이다. 만약 쿼터니온으로 방향을 저장한다면, 쿼터니온에 각의 속도(velocities)를 추가하는 것이 행렬에 추가하는 것보다 계산적으로 더 적은 비용을 소비하게 할 것이다.

Tomb Raider

Tomb Raider 타이틀은 카메라 움직임을 애니메이션하기 위해서 쿼터니온 회전을 사용한다. 

 

오브젝트의 방향을 표현하기 위한 많은 방식이 존재한다. 대부분의 프로그래머들은 3x3 회전 행렬이나 세 개의 오일러(Euler) 각을 사용해 이 정보를 저장한다. 이러한 해결책들은 오브젝트의 두 방향 사이를 부드럽게 보간하려고 하기 전에는 잘 동작한다. 사용자가 제어하지 않고 단순히 공간을 자유롭게 회전하는 오브젝트를 상상해 보자(예를 들어 회전문). 만약 당신이 문의 방향을 회전 행렬이나 오일러 각으로 저장하기로 한다면, 당신은 회전 행렬의 값들을 부드럽게 보간하는 것은 계산적으로 비용이 많이 들고 쿼터니온 보간보다는 플레이어의 시점에 대해서 정확하고 부드럽게 보이지 않는다는 것을 알게 될 것이다.

 

이 문제를 행렬이나 오일러 각을 사용해 해결하고자 한다면, 애니메이터는 단순히 기정의된(keyed) 방향의 숫자를 증가시키기만 해야 할 것이다. 그러나 아무도 얼마나 많은 방향이 충분한 것인지 알 수는 없다. 왜냐하면 게임은 서로 다른 컴퓨터 상에서 서로 다른 프레임율로 작동하며, 회전의 부드러움에 영향을 줄 수 있기 때문이다. 이때가 쿼터니온을 사용할 적절한 시점이며, 이 기법은 우리의 회전문과 같은 오브젝트의 단순한 회전을 표현하기 위해서 단지 두/세개의 방향만을 요구한다. 또한 당신은 개별적인 프레임율에 따라서 보간되는 위치의 개수를 동적으로 조정할 수도 있다.

 쿼터니온 이론과 응용프로그램에 대해서 살펴보기 전에 회전이 표현되는 방식에 대해서 살펴보도록 하자. 나는 회전 행렬, 오일러각, 축, 각 표현과 같은 기법에 대해서 다룰 것이며, 그것들의 단점 및 쿼터니온과의 관계에 대해서 설명할 것이다. 만약 이러한 기법들에 대해서 익숙하지 않다면 그래픽 책을 펴서 공부하기를 바란다.


지금까지 나는 4x4 나 3x3 행렬을 사용하는 회전에 대해서 이야기하지 않는 3D 그래픽 책을 본 적이 없다. 결국 나는 대부분의 게임 프로그래머들이 이 기법에 대해 매우 친숙하다고 가정할 것이고, 나는 그것의 단점에 대해서만 이야기하도록 하겠다. 또한 Game Developer 에 1997년 6월에 작성된 Chris Hecker 의 기사("Physics, Part 4 : The Third Dimension," 페이지 15-26)를 다시 읽어볼 것을 강력히 추천한다. 왜냐하면 그것은 3D 오브젝트의 방향설정(orenting)의 문제에 대해서 문제제기하기 때문이다.

 

회전은 단지 x, y, z 좌표축을 중심으로 한 세 개의 자유각(degrees of freedom, DOF)만을 포함한다. 그러나 9 DOF(3x3 행렬이라 가정)는 회전에 제약을 가할 것을 요구한다 - 우리가 필요로하는 것보다 더 많이. 또한 행렬은 "미끄러지는(drifting)" 경향이 있다. 이것은 6개의 제약(constraint) 중 하나가 위반되고 행렬이 상대적인 축을 중심으로 한 회전을 설명할 때 이러한 상황이 발생한다.

 

이 문제와 싸우는 것은 행렬 직교화(orthonormalized)를 유지할 것을 요구한다 - 그것은 제약에 순종하는 것을 보증한다. 그러나 이렇게 하는 것은 계산적인 낭비이다. 행렬 미끄러짐을 해결하기 위한 일반적인 방법은 상대적인 기반(basis)를 직교 기반으로 변환하기 위한 Gram-Schmidt 알고리즘에 의존한다. Gram-Schmidt 알고리즘이나 행렬 미끄러짐을 해결하기 위해서 정확한 행렬을 계산하는 것은 많은 CPU 사이클을 소비하며, 부동 소수점 수학을 사용하고 있음에도 불구하고 매우 자주 수행되어야만 한다.

 

회전 행렬의 또 다른 단점은 그것들이 두 개의 방향 사이의 회전을 보간하기에는 사용하기 너무 어렵다는 것이다. 또한 결과 보간은 가시적으로 매우 변덕스러우며, 이것은 게임에 더 이상 적절치 않음을 의미한다.

당신은 세 좌표축에 대한 회전을 표현하기 위해서 각을 사용할 수도 있다. 이것을 (q, c, f) 식으로 기록할 수 있다; "q 각만큼 z 축 중심으로 반시계 방향으로 회전함으로써 점을 변환한다, 그리고 나서 c 각만큼 y 축 중심으로 회전하고, f 각만큼 x 축 중심으로 회전한다"라는 단순한 상태이다. 여기에는 당신이 오일러 각을 사용하여 회전을 표현하기 위해서 사용할 수 있는 12개의 규약(convention)이 존재한다. 왜냐하면 당신은 회전(XYZ, XYX, XYY...)을 표현하기 위해서 각들의 조합을 사용할 수 있기 때문이다. 우리는 첫 번째 규약 (XYZ)를 제출할 모든 예제를 위한 규약으로 간주한다. 나는 양의(positive) 회전은 모두 반시계 방향이라고 간주할 것이다(Figure 1).

오일러 각 표현은 매우 효율적이다. 왜냐하면 그것은 단지 세 개의 변수만을 사용해 세 개의 DOF 를 표현하기 때문이다. 또한 오일러 각은 모든 제약에 복종할 필요가 없으며, 결국 미끌어지는 경향도 없고 재조정될 필요도 없다.

그러나 연속되는 회전 결합과 관련되어 오일러 각을 사용한 단일 회전을 표현하는 쉬운 방법은 없다. 더우기 두 방향 사이의 부드러운 보간은 수치적 통합(numerical integration)을 포함하는데, 이것은 계산적으로 낭비이다. 또한 오일러 각은 "Gimbal lock" 문제나 a loss of one degree of rotational freedom 이라는 문제를 안고 있다. Gimbal lock 90도로 연속되는 회전이 수행될 때 발생된다; 갑자기 회전이 발생하지 않게 된다. 축에 정렬되어버리기 때문이다.

Figure 1
Figure 1: 오일러 각 표현.

예를 들어 비행 시뮬레이션에 의해서 연속되는 회전이 수행되었다고 상상해 보자. 당신은 첫 번째 회전을 x 축 중심의 Q1 이라고 지정하고, 두 번째 회전을 y 축 중심의 90 도 라고 지정하고, 세 번째 회전을 z 축 중심의 Q3 라고 지정했다고 하자. 만약 지정된 회전이 성공한다면, 당신은 z 축 중심의 Q3 회전이 초기 x 축 중심의 회전과 같은 효과를 가지고 있음을 발견하게 될 것이다. y 축 회전은 x 축과 z 축을 정렬되도록 만들어 버렸으며, 당신은 DOF 를 잃어버리게 되었다. 왜냐하면 한 축을 중심으로 하는 회전이 다른 축을 중심으로 하는 반대 회전과 같기 때문이다. Gimbal lock 문제에 대한 세부적인 논의를 알기 위해서는 Advanced Animation and Rendering Techniques : Theory and Practice by Alan and Mark Watt(Addison Wesley, 1992) 를 읽어볼 것을 강력히 추천한다.

축과 각을 사용하는 표현은 회전 표현의 또 다른 방식이다. 당신은 Figure 2 에서 보이는 것 처럼 상대적 축과 각을 지정한다(만약 반시계방향이라면 양의 회전임).

이것은 회전을 표현하기 위한 효율적인 방식이지만, 그것은 오일러 각 표현을 설명했던 것과 (Gimbal lock 문제를 제외하고는) 같은 문제를 내포한다

Figure 2

18세기에 W.R.Hamilton 은 복잡한 숫자에 대한 4차원 확장으로써 쿼터니온을 고안했다. 이후에 쿼터니온이 회전과 방향을 3차원에서 표현할 수도 있음이 증명되었다. 쿼터니온을 표현하기 위해서 사용할 수 있는 몇 가지 공식이 있다. 두 개의 가장 유명한 공식은 complex number 공식(Eq. 1) 과 4D vector 공식(Eq. 2)이다.

 

w + xi + yj + zk (i2 = j2 = k2 = -1 이며 ij = k = -ji 이고, w, x, y, z 는 실제 값이다.) 
(Eq. 1) 

[w, v] (v = (x, y, z) 는 "벡터" 라 불리며 w 는 "스칼라"라고 불린다) 
(Eq. 2) 

나는 두 번째 공식을 이 기사 전반에서 사용할 것이다. 쿼터니온이 표현되는 방식에 대해서 알게 되었으니, 그것들을 사용하는 기본적인 연산을 배워보자.

 


만약 q 와 q' 가 쿼터니온에 의해 표현되는 두 개의 방향이라고 한다면, 당신은 이들 쿼터니온에 Table 1 에 있는 연산을 정의할 수 있다.

모든 다른 연산들은 이들 기본적인 것들로부터 쉽게 이끌어내질 수 있으며, 그것들은 여기에 동봉된 라이브러리에서 세부적으로 설명해 놓았다. 나는 단지 단위 쿼터니온만을 다룰 것이다. 각 쿼터니온은 4D 공간으로 이동될 수 있으며(왜냐하면 각 쿼터니온은 네 개의 부분으로 구성되기 때문이다) 그 공간은 쿼터니온 공간이라고 불린다. 단위 쿼터니온은 그것들의 크기(magnitude)각 1이고 그것은 쿼터니온 공간의 하위 공간인 S3를 형성한다. 이 하위공간은 4D 구체로서 표현될 수 있다. (그것들은 하나의 단위 법선을 가진다) 이것은 당신이 수행해야만 하는 필수 연산들의 개수를 감소시켜 준다.

단지 단위 쿼터니온들만이 회전을 표현하고, 내가 쿼터니온에 대해서 이야기할 때 다른 것이 아니라 단위 쿼터니온에 대해서 이야기하고 있는 것이라고 생각하는 것은 매우 중요하다.

당신은 다른 기법들이 회전을 표현하는 방식에 대해서 이미 살펴 보았기 대문에, 쿼터니온을 사용해 회전을 지정할 수 있는 방법에 대해서 살펴 보도록 하자. 단위 쿼터니온 q 에 의한 벡터 v 의 회전은 다음과 같이 표현될 수 있다는 것이 증명될 수 있다(그리고 증명은 그렇게 어렵지 않다).

v´ = q v q-1 (v = [0, v]) 
(Eq. 3) 

결과는 회전된 벡터 v' 이며, w 를 위해서 항상 0 스칼라 값을 가지게 될 것이다(이전의 Eq. 2를 재호출). 그래서 당신은 계산을 생략할 수 있다.

Table 1. 쿼터니온을 사용한 기본 연산.

Addition: q + q´ = [w + w´, v + v´] 

Multiplication: qq´ = [ww´ - v · v´, v x v´ + wv´ +w´v] (· 은 벡터 내적이며  x 는 벡터 외적이다); Note: qq´ ? q´q


Conjugate: q* = [w, -v] 

Norm: N(q) = w2 + x2 + y2 + z2 

Inverse: q-1 = q* / N(q) 

Unit Quaternion: qN(q) = 1 이고 q - 1 = q* 일때 q 는 단위 쿼터니온이다

Identity: [1, (0, 0, 0)] (when involving multiplication) and [0, (0, 0, 0)] (when involving addition) 


오늘날 대부분 지원하는 Direct3D Immediate 모드(retained 모드는 쿼터니온 회전의 제한된 집합을 가진다) 와 OpenGL 의 API는 쿼터니온을 직접적으로 지원하지 않는다. 결과적으로 당신은 이 정보를 당신이 선호하는 API 에 넘기기 위해서 쿼터니온 방향을 변환할 필요가 있다. OpenGL 과 Direct3D 모두 당신에게 회전을 행렬로 지정하는 방법을 제공하며, 쿼터니온에서 행렬로 변환하는 루틴이 유용하다. 또한 당신이 회전을 일련의 쿼터니온으로서 저장하지 않는 (NewTek 의 LightWave 와 같은) 그래픽 패키지로부터 씬 정보를 import 하기를 원한다면, 당신은 쿼터니온 공간으로부터 혹은 공간으로 변환하는 방법을 필요로 하게 될 것이다.

각 과 축. 각과 축 공식으로부터 쿼터니온 공식으로 변환하는 것은 두 개의 삼각형(trigonometric) 연산과 함께 몇몇 곱셈과 나눗셈을 포함한다. 그것은 다음과 같이 표현될 수 있다.

q = [cos(Q/2), sin(Q /2)v] (Q 는 각이며 v는 축이다) 
(Eq. 4) 

오일러 각. 오일러 각을 쿼터니온으로 변환하는 것은 유사한 과정이다 - 당신은 단지 정확한 순서로 연산을 수행하는 데 주의하기만 하면 도니다. 예를 들어 비행 시뮬레이션에서 비행기가 yaw, pitch, roll 순으로 작업을 수행했다고 하자. 당신은 이 결합된 쿼터니온 회전을 다음과 같이 표현할 수 있다


q = qyaw qpitch qroll 에서: 
               qroll = [cos (y/2), (sin(y/2), 0, 0)] 
               qpitch = [cos (q/2), (0, sin(q/2), 0)] 
               qyaw = [cos(f /2), (0, 0, sin(f /2)] 
(Eq. 5) 

곱셈을 수행하는 순서는 중요하다. 쿼터니온 곱셈에는 교환법칙이 성립하지 않는다(벡터 내적을 포함하고 있기 때문이다). 다시 말해 당신이 오브젝트를 다양한 각에 대해서 회전하는 순서를 바꾸게 된다면, 그것은 다른 결과 방향을 생성할 수 있으며, 결국 순서가 매우 중요하다.

회전 행렬. 회전 행렬에서 쿼터니온 표현으로 변환하는 것은 일이 좀 더 많으며, 그것의 구현은 Listing 1 에 나와 있다.

단위 쿼터니온과 회전 행렬 사이의 변환은 다음과 같이 지정될 수 있다.

 


(Eq. 6)

 

쿼터니온을 사용해서 회전을 직접 지정하는 것은 어렵다. 당신의 캐릭터나 오브젝트의 방향을 오일러 각으로 젖아하고 그것을 보간 전에 쿼터니온으로 변환하는 것이 최선이다. 사용자의 입력을 받은 이후에 쿼터니온을 직접 재계산하는 것보다는 오일러 각을 사용해 회전을 각으로 증가시키는 것이 더 쉽다(즉 roll = roll + 1).

 

쿼너티온, 회전 행렬, 오일러 각 사이의 변환은 매우 자주 수행되기 때문에, 변환 과정을 최적화하는 것이 매우 중요하다. 단위 쿼터니온과 행렬 사이의 (9개의 곱셈만을 포함하는) 매우 빠른 변환은 Listing 2 에 나와 있다. 그 코드는 행렬이 오른손 좌표계에 있으며 행렬 회전이 열우선 순으로 표현된다고 가정한다는 데 주의하라(예를 들어 OpenGL 호환).

Listing 1: 행렬에서 쿼터니온으로 변환하는 코드.

MatToQuat(float m[4][4], QUAT * quat)
{
  float  tr, s, q[4];
  int    i, j, k;


  int nxt[3] = {1, 2, 0};


  tr = m[0][0] + m[1][1] + m[2][2];


  // check the diagonal
  if (tr > 0.0) {
    s = sqrt (tr + 1.0);
    quat->w = s / 2.0;
    s = 0.5 / s;
    quat->x = (m[1][2] - m[2][1]) * s;
    quat->y = (m[2][0] - m[0][2]) * s;
    quat->z = (m[0][1] - m[1][0]) * s;
} else {  
  // diagonal is negative
       i = 0;
          if (m[1][1] > m[0][0]) i = 1;
      if (m[2][2] > m[i][i]) i = 2;
            j = nxt[i];
            k = nxt[j];


            s = sqrt ((m[i][i] - (m[j][j] + m[k][k])) + 1.0);
      
      q[i] = s * 0.5;
            
            if (s != 0.0) s = 0.5 / s;


     q[3] = (m[j][k] - m[k][j]) * s;
            q[j] = (m[i][j] + m[j][i]) * s;
            q[k] = (m[i][k] + m[k][i]) * s;


   quat->x = q[0];
   quat->y = q[1];
   quat->z = q[2];
   quat->w = q[3];
  }
}

만약 당신이 단위 쿼터니온을 다루고 있지 않다면, 부가적인 곱셈 및 나눗셈이 요구된다. 오일러 각을 쿼터니온으로 변환하는 것은 Listing 3 에 나와 있다. 게임 프로그래머에 있어서 쿼터니온의 가장 강력한 장점 중 하나는 두 개의 쿼터니온 방향 사이의 보간이 매우 쉬우며 부드러운 애니메이션을 생성할 수 있다는 것이다. 이게 왜 그런지 설명하기 위해서 구체 회전을 사용한 예제를 살펴 보자. 구체 회전 보간은 4 차원에서 최단 경로(arc)인 단위 쿼터니온 구체를 따른다. 4D 구체는 상상하기가 어렵다. 나는 3D 구체(Figure 3)를 사용해 쿼터니온 회전과 보간을 가시화하도록 하겠다.


구체의 중심으로부터 애니메이션되고 있는 벡터의 초기 방향은 q1 에 의해서 표현되고 벡터의 최종 방향은 q3 에 의해서 표현된다고 가정하자. q1 과 q3 사이의 호(arc)는 보간이 따라가는 경로이다. Figure 3 은 우리가 중간 위치인 q2 를 가지고 있다면, q1 -> q2 -> q3 의 보간 경로가 q1 -> q3 보간의 경로와 반드시 같지는 않을 것이다. 초기 및 최종 방향은 같지만 경로는 같지 않다.

쿼터니온은 회전 혼합시에 요구되는 계산을 단순화한다. 예를 들어 당신이 행렬로서 표현되는 두 개 이상의 방향을 가지고 있다면, 두 개의 중간 회전을 곱함으로써 그것들을 쉽게 결합할 수 있다.

R = R2R1 (회전 R1 다음에 회전 R2 가 옴) 
(Eq. 7) 


Listing 2: 쿼터니온을 행렬로 변환. ([07.30.02] 역자 노트 : 다음 QuatToMatrix 함수는 초기에는 버그가 있었다 -- 열과 행의 순서가 바뀌어 있었다. 이것은 교정된 버전이다. 이것을 지적해 준 John Ratcliff 과 Eric Haines 에게 감사한다.)

 

QuatToMatrix(QUAT * quat, float m[4][4]){

float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;


// 계수 계산
x2 = quat->x + quat->x; y2 = quat->y + quat->y; 
z2 = quat->z + quat->z;
xx = quat->x * x2; xy = quat->x * y2; xz = quat->x * z2;
yy = quat->y * y2; yz = quat->y * z2; zz = quat->z * z2;
wx = quat->w * x2; wy = quat->w * y2; wz = quat->w * z2;


m[0][0] = 1.0 - (yy + zz); m[1][0] = xy - wz;
m[2][0] = xz + wy; m[3][0] = 0.0;

m[0][1] = xy + wz; m[1][1] = 1.0 - (xx + zz);
m[2][1] = yz - wx; m[3][1] = 0.0;


m[0][2] = xz - wy; m[1][2] = yz + wx;
m[2][2] = 1.0 - (xx + yy); m[3][2] = 0.0;


m[0][3] = 0; m[1][3] = 0;
m[2][3] = 0; m[3][3] = 1;

}

 

이 결합(composition)은 27 개의 곱셈과 18 개의 덧셈을 포함하며, 3x3 행렬로 간주한다. 다시 말해 쿼터니온 결합은 다음과 같이 표현될 수 있다.


q = q2q1 (회전 q1 다음에 회전 q2 가 온다) 
(Eq. 8) 

이미 살펴 보았듯이 쿼너티온 기법은 행렬 결합과 유사하다. 그러나 쿼터니온 기법은 단지 8 개의 곱셈과 4 개의 나눗셈만을 요구한다(Listing 4). 그래서 쿼터니온을 결합하는 것은 계산적으로 행렬 결합보다 비용이 싸다. 이러한 비용을 아끼는 것은 계층적 오브젝트 표현과 역운동학에 있어서 특히 중요하다.

 

이제 효율적인 곱하기 루틴을 가지게 되었다. 가장 짧은 호를 따라서 두 쿼터니온 회전을 보간하는 방법에 대해서 살펴 보자. 구형 선형 보간(Spherical Linear intERPolation(SLERP)) 가 이를 수행하며 다음과 같이 작성될 수 있다. 


(Eq. 9)

여기에서 pq = cos(q) 와 인자 t 는 0 부터 1 사이의 값이다. 이 공식의 구현은 Listing 5 에 제출되어 있다. 만약 두 개의 방향이 너무 가깝다면, 당신은 0 으로 나누는 것을 막기 위해서 선형 보간을 사용할 수 있다.


Figure 3. 쿼터니온 회전.

 

Listing 3: 오일러각을 쿼터니온으로 변환.

EulerToQuat(float roll, float pitch, float yaw, QUAT * quat)

{

    float cr, cp, cy, sr, sp, sy, cpcy, spsy;

    // calculate trig identities

    cr = cos(roll/2); cp = cos(pitch/2); 

    cy = cos(yaw/2); sr = sin(roll/2); 

    sp = sin(pitch/2); 

    sy = sin(yaw/2); 

    cpcy = cp * cy; 

    spsy = sp * sy; 

    quat->w = cr * cpcy + sr * spsy; 

    quat->x = sr * cpcy - cr * spsy; 

    quat->y = cr * sp * cy + sr * cp * sy; 

    quat->z = cr * cp * sy - sr * sp * cy;

}

 

기본 SLERP 회전 알고리즘은 Listing 6 에 나와 있다. 당신의 회전 표현이 상대적인 회전이 아니라 절대적인 회전이어야만 함에 주의하라. 상대적 회전은 이전의 (중간) 방향으로부터의 회전이라고 생각할 수 있으며, 절대적 회전은 초기 방향으로부터의 회전이라고 생각할 수 있다. 이것은 당신이 Figure 3 의 q2 쿼터니온방향을 상대 회전으로 생각하면 명확해 진다. 왜냐하면 그것은 q1 방향에 대해 상대적으로 움직였기 때문이다. 주어진 쿼터니온의 절대 회전을 획득하기 위해서는 단지 현재 상대 회전에 이전 상대 회전을 곱하기만 하면 된다. 오브젝트의 초기 방향은 단위 곱 [1, (0, 0, 0)]으로서 표현될 수 있다. 이것은 첫 번째 방향이 항상 절대적인 것임을 의미한다. 왜냐하면 

q = qidentity q 
(Eq. 10) 

이기 때문이다.


Listing 4: 효율적인 쿼터니온 곱.

 

QuatMul(QUAT *q1, QUAT *q2, QUAT *res)

    float A, B, C, D, E, F, G, H; 

    A = (q1->w + q1->x)*(q2->w + q2->x); 

    B = (q1->z - q1->y)*(q2->y - q2->z); 

    C = (q1->w - q1->x)*(q2->y + q2->z); 

    D = (q1->y + q1->z)*(q2->w - q2->x); 

    E = (q1->x + q1->z)*(q2->x + q2->y); 

    F = (q1->x - q1->z)*(q2->x - q2->y); 

    G = (q1->w + q1->y)*(q2->w - q2->z); 

    H = (q1->w - q1->y)*(q2->w + q2->z); 

 

    res->w = B + (-E - F + G + H) /2; 

    res->x = A - (E + F + G + H)/2; 

    res->y = C + (E - F + G - H)/2; 

    res->z = D + (E - F - G + H)/2;

}

 

이전에 언급했듯이 쿼터니온의 실제 용도는 삼인칭 게임에서의 카메라 회전을 포함한다. 나는 TOMB RAIDER 에서의 카메라 구현을 본 이후로 나는 비슷한 것을 구현하기를 원했다. 자 삼인칭 카메라를 구현해 보자(Figure 4).

먼저 항상 캐릭터의 머리 위쪽에 위치하고, 캐릭터의 머리 약간 위쪽을  바라보는 카메라를 생성하자. 또한 그 카메라는 메인 캐릭터의 뒤에서 d 단위만큼 떨어져 있다. 또한 x 축 중심으로 회전함으로써 roll (Figure 4 의 각 q)을 변경하도록 구현할 수도 있다.


플레이어가 캐릭터의 방향을 바꾸면, 당신은 캐릭터를 즉시 회전시키고 SLERP 를 사용해 캐릭터의 뒤에서 카메라의 방향을 재설정한다(Figure 5). 이것은 부드러운 카메라 회전을 제공하고 플레이어로 하여금 게임이 그들의 입력에 즉각 반응하는 것처럼 느끼게 만드는 두 가지 이점을 가지고 있다.


Figure 4. Third-person camera. 


Figure 5. Camera from top.

 

당신은 카메라의 회전 중심(pivot point) 을 그것이 따라 움직이는 오브젝트의 중심으로 설정할 수 있다. 이것은 당신이 캐릭터가 게임 월드 안에서 이동할 때 이미 만들어낸 계산을 이용할 수 있도록 해 준다.

 

1인칭 액션 게임에서는 쿼터니온 보간을 이용하지 말라고 하고 싶다. 왜냐하면 이들 게임은 일반적으로 플레이어의 행동에 대한 즉각적인 반응을 요구하며, SLERP 는 시간을 소비하기 때문이다.


그러나 우리는 그것을 특별한 씬에서 사용할 수 있다. 예를 들어 당신이 탱크 시뮬레이션을 만들고 있다고 하자. 모든 탱크는 scope나 targeting 메커니즘을 가지고 있으며, 당신은 그것을 가능한한 실제적으로 시뮬레이션하고 싶을 것이다. scoping 메커니즘과 탱크의 포신은 플레이어가 제어하는 모터 집합에 의해서 제어된다. scope 의 줌(zoom) 능력과 대상 오브젝트의 거리에 의존해, 모터의 작은 움직임이더라도 가시 각에 있어서는 큰 차이를 보일 수 있으며, 개별 프레임 사이에서 약간 끊기거나 큰 움직임을 보일 수 있다. 이러한 원하지 않는 효과를 제거하기 위해서는 줌과 오브젝트의 거리에 관련한 방향 보간을 수행할 수 있다. 몇 프레임 동안 두 위치 사이를 보간하는 것은 빠른 움직임을 완충하고 플레이어가 방향감각을 상실하지 않게 만들어 준다.

쿼터니온을 사용하는 또다른 유용한 응용프로그램은 이미 기록된 (그러나 렌더링되지 않은) 애니메이션이다. (최근의 많은 게임들처럼) 게임을 플레이해 보면서 카메라의 움직임을 기록하는 대신에 Softimage 3D 나 3D Studio MAX 와 같은 상업 패키지를 사용해 카메라의 움직임이나 회전을 미리 기록할 수 있다. 이것은 공간과 렌더링 시간을 절약해 준다. 그리고 나서 당신은 영화같은 씬을 위한 스크립트 호출이 있을 때마다 키프레임 카메라 모션을 재생하기만 하면 된다.

Listing 5: SLERP 구현. 

QuatSlerp(QUAT * from, QUAT * to, float t, QUAT * res)

{

    float to1[4]; 

    double omega, cosom, sinom, scale0, scale1; 

    // calc cosine 

    cosom = from->x * to->x + from->y * to->y + from->z * to->z + from->w * to->w; 

    // adjust signs 

    (if necessary) 

        if ( cosom <0.0 )

        

            cosom = -cosom; 

            to1[0] = - to->x; 

            to1[1] = - to->y; 

            to1[2] = - to->z; 

            to1[3] = - to->w; 

        }

        else 

        

            to1[0] = to->x; 

            to1[1] = to->y; 

            to1[2] = to->z; 

            to1[3] = to->w; 

        }

        // calculate coefficients

        if ( (1.0 - cosom) > DELTA ) 

        

            // standard case

            (slerp) omega = acos(cosom); 

            sinom = sin(omega); 

            scale0 = sin((1.0 - t) * omega ) / sinom; 

            scale1 = sin(t * omega) / sinom; 

        }

        else

        { 

        // "from" and "to" quaternions are very close

        // ... so we can do a linear interpolation 

        scale0 = 1.0 - t; 

        scale1 = t; 

        }

        // calculate final values 

        res->x = scale0 * from->x + scale1 * to1[0]; 

        res->y = scale0 * from->y + scale1 * to1[1]; 

        res->z = scale0 * from->z + scale1 * to1[2]; 

        res->w = scale0 * from->w + scale1 * to1[3];

}

 

작년에 Chris Hecker 의 물리 관련 칼럼을 읽은 후에, 나는 내가 작업하고 있던 게임 엔진에 각속도(angular velocity)를 추가하고 싶어졌다. Chris 는 주로 행렬 수학에 대해 다뤘는데, 나는 쿼터니온 에서 행렬로 행렬에서 쿼터니온으로 변환하는 것을 줄이고자 했기 때문에(우리 게임엔진은 쿼터니온 수학에 기반하고 있다), 나는 약간을 연구를 통해 쿼터니온 방향을 위한 (벡터로 표현된) 각속도를 추가하는게 쉽다는 것을 발견했다. 그 해결책(Eq. 11)은 다음과 같은 미분방정식으로 표현될 수 있다.

 



(Eq. 11)

 

여기에서 quat(angluar) 는 0 스칼라 부분을 가지고 있는 (즉 w = 0) 쿼터니온이며 벡터 부분은 각속도 벡터와 같다. Q 는 원래 쿼터니온 방향이다.

 

위의 쿼터니온 (Q + dQ / dt) 를 통합하기 위해서, 나는 Runge-Kutta order four method을 사용할 것을 추천한다. 만약 당신이 행렬을 사용하고 있다면, Runge-Kutta order five method을 사용해 더 좋은 결과를 산출할 수 있을 것이다. (Runge-Kutta method 는 특별한 공식을 통합하는 방식이다. 이 기법에 대한 자세한 설명은 Numerical Recipes in C 와 같은 모든 기초 수치 알고리즘 책에서 찾아볼 수 있다. 그것은 수치적, 미분 방정식을 주제로한 세부적인 섹션을 포함하고 있다.) 각속도 통합의 완벽한 유도(derivation)을 원한다면 Dave Baraff 의 SIGGRAPH 튜토리얼을 찾아보기 바란다.

쿼터니온은 회전을 저장하고 수행하기 위한 매우 효율적이며 극단적으로 유용한 기법이다. 그리고 그것들은 다른 메서드들에 비해서 많은 이점을 제공한다. 불운하게도 그것들은 가시화될 수 없으며, 매우 직관적이지 못하다. 그러나 만약 당신이 내부적으로는 쿼터니온을 사용해 회전을 표현하고, 직접 표현으로서 약간의 다른 메서드들(예를 들어 각-축 또는 오일러각) 사용한다면, 당신은 그것들을 가시화할 필요가 없어질 것이다. 


Nick Bobick Caged Entertainment Inc 의 게임 개발자이다. 그리고 그는 현재 멋진 3D game 작업을 하고 있다. 그의 메일은  nb@netcom.ca 이다. 저자는 그의 연구와 출판을 위해서 도움을 줌 Ken Shoemake 에게 감사의 인사를 보내길 원한다 그가 없었다면 이 기사는 불가능했을 것이다. 


반응형

Catmull-Rom 스플라인 곡선은 Edwin Catmull 과Raphael Rom에 의해 개발 되었으며 

통과점만으로부터 매끄러운 곡선을 정의하는 방법중에 하나이다


베지어 곡선은 중간중간의 Point를 지나지 않으며 Point의 영향으로 휘어지는 곡선인 반면 

Catmull-Rom 곡선은 모든 Point를 지나도록 되어있다.


Ferguson / Coons 곡선과 같이 속도를 줄 필요 없이 제어점 만으로 곡선을 결정할수 있으므로 

칼의 궤적같은 형태를 만들거나 할때 자주 쓰인다。






그 밖에 AI 캐릭터가 이동경로를 부드럽게 움직이도록 사용하거나 키프레임들 사이에 부드러운 보간을 얻는데 사용한다




Catmull - Rom 스플라인 곡선의 매개 변수 방정식은 다음과 같으며


 P(t) = 0.5 * ((2.0 * P1) + (-P0 + P2) * t + (2.0 * P0 - 5.0 * P1 + 4.0 * P2 - P3) * t^2 +

         (-P0 + 3.0 * P1 - 3.0 * P2 + P3) * t^3); 


위의 그림처럼 P2와 P3 사이의 곡선을 구하고 싶은 경우 초기위치 마지막위치는 이전과 마찬가지로 P2, P3가 된다



초기속도는 P2의 전후 점인 P1, P3에 의해 결정된다.


이 값은 벡터 P1, P3의 절반이다.


최종속도도 마찬가지로 벡터 P2, P4의 절반이다.


이걸로 초기위치 최종 위치, 및 초기속도 최종속도를 알 수 있으므로

Ferguson / Coons 곡선 처럼 해서 풀수 있다



위 그림의 B와 C의 Catmull-Rom 스플라인 곡선을 구한다.

우선 속도를 구한다.


이것을 위에서 설명한 Catmull- Rom 매개변수 방정식에 대입한다

(위의 식과는 약간 변형된 형태)





가 된다. 이걸로 t를 0부터 1까지 움직이면 BC 간에 곡선을 그린다

실제로 그래프에 그려보면 아래와 같이 된다




현재 DirectX 에서는 아래와 같은 함수를 제공하고있다


 D3DXVec2CatmullRom(D3DXVECTOR2* pOut,
                   CONST D3DXVECTOR2* pV1,
                   CONST D3DXVECTOR2* pV2,
                   CONST D3DXVECTOR2* pV3,
                   CONST D3DXVECTOR2* pV4,
                   FLOAT s)

D3DXVec3CatmullRom(D3DXVECTOR3* pOut,
                   CONST D3DXVECTOR3* pV1,
                   CONST D3DXVECTOR3* pV2,
                   CONST D3DXVECTOR3* pV3,
                   CONST D3DXVECTOR3* pV4,
                   FLOAT s)

D3DXVec4CatmullRom(D3DXVECTOR4* pOut,
                   CONST D3DXVECTOR4* pV1,
                   CONST D3DXVECTOR4* pV2,
                   CONST D3DXVECTOR4* pV3,
                   CONST D3DXVECTOR4* pV4,
                   FLOAT s)


위 함수들은 각각 4개의 점과 0~1사이의 하나의 가중치 값(s)을 받으며

pOut에 보간 된 벡터를 반환한다


만약 s 가 0이면 pv2를 리턴하게 되며 1일 경우 pv3를 리턴하게 된다


그외에도 다양한 보간법들이 있을 것이다..


예를들면.. Bicubic 이라던지...


http://en.wikipedia.org/wiki/Bicubic_interpolation

http://scosco.com.ne.kr/Stereo3DHtml/vr_0002_bicubic.htm


http://cafe.naver.com/devrookie/6085

출처블로그>Vanica's Lifelog - 夢が夢で終わらないように | 풍풍풍

원문http://blog.naver.com/sorkelf/40154552485



반응형

분리축 SAT 충돌처리



obb 충돌처리.pdf

반응형


http://www.gingaminga.com/Data/Note/oriented_bounding_boxes/


Object Oriented Bounding Box  이용한 Collision Detection

96419-044

원선

출처; http://mimosa.snu.ac.kr/~rabbit2/

 

<목차>

  1. 소개글
  2. 이론
  3. 코드 다운로드  사용법
  4. 참고 문헌
  5. 웹페이지 다운로드

 

1. 소개글

지금까지 리서치 주제로 BSP tree 라던지 bounding sphere에 의한 collision detection(이하 CD)은 많이 있었는데그에 비해 bounding box에 의한 CD에 대한 글은 없었던 것 같다물론, bounding sphere에 의한 CD의 쓰임새도 많지만(예를 들어 당구공에 의한 CD라던지), 생각해 보면 우리 주위에 있는 물체 중에서 둥근 것보다는 네모난 것이 훨씬 더 많다는 것을 알 수 있다만일 직육면체에 가까운 물체를 bounding sphere로 모델링하게 되면밑에 그림에서 볼 수 있듯이 많은 공간이 남게 되고이 공간을 줄이기 위해서는 depth가 여러 레벨인 sphere tree를 구성할 수 밖에 없다그럼에도 불구하고 bounding sphere가 널리 이용되는 가장 큰 이유는, bounding sphere가 구현하기가 가장 쉽기 때문인 것 같다. Bounding sphere로 일단 모델링하고 나면 CD를 하기 위해서 단순히 두 구 사이의 거리를 측정해서 각 구의 반지름의 합과 비교하기만 하면 된다.
Bounding box
에 의한 모델링은 axis-aligned bounding box(이하 AABB) object-oriented bounding box(이하 OBB) 두 가지 방법이 있다. AABB bounding box를 잡을 때 항상 world coordinate system 3개의 축과 평행한 방향으로만 잡는 것이고이것은 bounding sphere와 마찬가지로 구현하기는 매우 쉬우나역시 물체가 world coordinate system 축과 다른 방향으로 놓이게 되면 많은 빈공간이 생기게 되어 효율이 급격하게 떨어지게 된다당연히 직육면체 모양의 물체를 가장 효율적으로 모델링하기 위해서는 그 물체가 놓여 있는 방향으로 bounding box를 잡는 것인데이것이 바로 OBB이다하지만 OBB는 구현하기가 비교적 난해하므로 지금까지 리서치가 안 되었었던 것 같은데필자는 최대한 잘 설명하려고 노력하겠다 -_-;;;

<bounding sphere>

직육면체의 물체를 모델링하기 위해서는 여러 개의 구가 필요하고구에빈공간이 생겨서 정확히 모델링이 되지 않는다.

 

 

<axis-aligned bounding box>

물체가 world coordinate axis와 다른 방향으로 놓여 있을 때 효율이 급격하게 떨어진다.

 

 

<object-oriented bounding box>

물체의 방향을 중심으로 bounding box를 잡기 때문에직육면체 모양의 물체를 가장 효율적으로 모델링할 수 있다.

anigray09_up.gif 위로

2. 이론

Mesh를 생성할 때 bounding box도 같이 생성하여, mesh transform 할 때 bounding box도 같이 transform을 해 주면 항상 모든 mesh에 대해 OBB를 유지할 수가 있다그렇다면 우리는 이제 mesh가 움직이면서 같이 돌아댕기는 여럿의 OBB를 갖고 있다이 때, OBB가 서로 충돌했는지는 어떻게 감지할 수가 있을까가장 무식한 방법은 두 개의 OBB의 모든 면과 모든 edge에 대해서 면을 통과하는 edge가 있는지 검사하는 방식일 것이다그런데 이 방식은 144번의 비교가 필요하고상당히 비싼 테스트이다여기서는 훨씬 더 효율적인 'axial projection'을 이용한 테스트를 소개한다.

Axial projection 이란 무엇인가?

두 개의 OBB가 서로 분리되어 있는지를 알기 위한 쉬운 방법 중 하나는 OBB를 공간상의 어떤 축(반드시 x,y,z축일 필요는 없다)에 투영하는 것이다이 투영을 'axial projection'이라고 하며이 투영을 통해 각 OBB는 축 상에 어떤 interval을 형성한다.만일 이렇게 형성된 2개의 interval이 서로 겹치지 않으면 2개의 OBB는 서로 분리되어 있는 것이 확실하며이때 이 축을 'separating axis'라고 한다만일 2개의 interval이 서로 겹친다면 2개의 OBB는 서로 분리되어 있을 수도 있고 아닐 수도 있기 때문에 더 많은 검사가 필요하다.

2개의 OBB가 충돌했는지 알기 위해서는 axial projection을 몇 번 해야 하는가?

(공리)
공간상의 2개의 분리된 convex한 다각면체는 1) 두 개의 다각면체 중 하나의 어느 면과 평행인 면또는 2) 두 개의 다각면체 각각에서 하나의 edge와 평행한 면에 의해 분리될 수 있다.

이 공리를 증명한 논문도 있긴 하지만그것은 관심있는 분은 찾아 보시고어쨌든 이 공리의 결과로서우리는 다음을 알 수 있다:
공간상의 2개의 convex한 다각면체가 분리되기 위한 필요충분 조건은, 1) 두 개의 다각면체 중 하나의 어느 면과 수직인 separating axis가 존재하거나, 2) 두 개의 다각면체 각각에서 하나의 edge와 수직인 separating axis가 존재하는 것이다.

 OBB 3개의 unique한 면 방향이 있고, 3개의 unique edge 방향이 있다따라서 위의 조건을 검사하기 위해서는 15개의 separating axis를 검사해야 한다.

(하나의 OBB에서 3개의 면다른 OBB에서 3개의 면, 9개의 2개의 OBB에서 edge들의 조합

(상자  A 의 축들에 대해서 3개, 상자 B 축들에 대해서 3개, 그리고 A 에서 한축 그리고 B 에서 하나를 사용하여 9개의 외적들을 만든것이 총 15개) : [Gottschalk , SIGRAPH 에 실린 내용]

만일 2개의 OBB가 서로 분리되어 있다면(충돌하지 않았다면) separating axis가 반드시 존재해야 하고 위에서 언급한 15개의 axis 중 하나가 그 axis가 되어야 한다만일 OBB들이 충돌했다면 separating axis가 존재하지를 않을 것이다따라서, 2개의 OBB의 충돌 여부를 검사하기 위해서는 15개의 seperating axis의 검사로 충분하다.

Separating axis 검사는 어떤 방식으로 할 수가 있는가?

이 검사를 하는 기본적인 전략은 다음과 같다:
1) 
 OBB의 중심을 해당 axis에 투영한다.
2) 
 OBB가 해당 axis에 투영되었을 interval radius(길이의 반)을 계산한다.
3) 
만일 해당 axis에 투영했을 때 OBB의 중심 사이의 거리가 각 OBB interval radius의 합보다 크면두 개의 OBB는 분리된 것으로 볼 수 있다.

밑에 있는 그림을 보면 이해가 더 쉽게 갈 것이다그림에서 A B는 각각 OBB이고 B A로부터 rotation R translation T만큼 이동한 위치에 있다. A B half dimension(또는 radius) 는 각각 ai, bi로 표기한다(i=1,2,3). A B에서 각 edge와 평행을 이루는 axis의 단위 벡터를 각각 Ai Bi라고 표기한다(i=1,2,3). 이렇게 해서 생긴 6개의 단위 벡터를 box axis라고 하자여기서 주목할 것은 A box axis basis로 사용하면회전 매트릭스 R 3개의 column(x,y,z 단위벡터를 transform 했을 때 나오는 벡터) 3개의 Bi axis 벡터와 같다는 사실이다.





<여기서 A B L에 투영하면 서로 분리된 interval이 되므로 L OBB A B에 대한 separating axis이다.>

보다시피 각 OBB의 중심은 투영된 interval의 중간에 투영된다 box radius axis L에 투영하고 투영된 길이의 합을 구함으로써 우리는 각 OBB의 투영된 interval을 구할 수가 있다위에서 OBB A interval radius는 다음과 같다:



OBB B에 대해서도 비슷한 식을 세울 수가 있다. Seperating axis의 위치는 검사에 아무런 영향을 끼치지 않으므로 우리는 axis가 A의 중심을 통과한다고 가정한다. 이 때, 2개의 interval 사이의 거리는   가 된다(그림 참고). 따라서, 2개의 interval 분리되어 있기 위한 필요충분 조건은 다음과 같다:



이 식은 액면으로는 복잡하지만, 15개의 separating axis에 적용하게 되면 L이 box axis이거나 box axis의 cross product가 되기 때문에 많이 단순화 된다. 예를 들어, 이라고 하자. 



위의 식에서 첫 번째 summation의 두 번째 항은 다음과 같이 단순화 된다.




마지막 단계는 전에 언급했듯이 B의 box axis는 A에 대한 B의 rotation matrix R의 column에 해당한다는 사실을 이용한 것이다. 이런 식으로 모든 항들은 단순화되고 때로는 값이 0이 되어 없어지기도 한다. 이렇게 단순화한 이후의 위의 seperating axis L에 대한 검사는 다음과 같이 된다:





모든 15개의 seperating axis에 대한 검사는 이런 방식으로 단순화되며, 이 검사의 과정에서 matrix R의 각 항은 4번씩 사용이 되므로 모든 검사를 시작하기 전에 matrix R을 미리 계산해 놓는다. 만일 이 검사 도중 식이 만족이 되면, seperating axis를 발견한 것이고, 그렇다면 2개의 OBB는 서로 분리되어 있음을 밝힌 것이 되기 때문에 나머지 검사는 할 필요가 없어진다. 아래의 표는 15개의 seperating axis에 대한 식을 정리한 것이다:






여기서 R0 항목은 OBB A interval radius, R1 항목은 OBB B interval radius, R 항목은 2개의 OBB의 중심 간의 투영된 거리를 나타낸다.
* D 
벡터는 2개의 OBB의 중심을 잇는 벡터를 나타낸다.
Cij 
 rotation matrix R에서의 (i,j항을 나타낸다.

anigray09_up.gif 위로

3. 코드 다운로드 및 사용법

aniapple_red.gif코드 다운로드

aniberry02_red.gif실행파일(exe) 다운로드

<실행방법>
 
그냥 가만히 실행시키고 기다리고 있으면 차가 건물에 가서 쿵.. 하고 박는다 -_- 화살표로 시점을 약간 변화시킬 수 있다.

<코드설명>
circle01_black.gifCBox 
클래스

CBox

Object oriented bounding box를 표현하는 클래스

CBox::center[3]

Bounding box의 중심 좌표

CBox::axis[3][3]

Bounding box 3 방향으로의 axis 벡터

CBox::extent[3]

Bounding box의 각 axis 벡터 방향으로의 radius

circle01_black.gifint BoxBoxIntersectionTest(const CBox& box0,const CBox& box1)
box0 
 box1이 서로 교차하는지 검사를 해서 교차를 하면 1, 그렇지 않으면 0을 리턴한다함수의 동작방식은 위의 표에 있는 15개의 seperating axis를 하나씩 검사하는 방식이다주석을 보고 표를 참고하면 이해하는데 아무런 어려움이 없을 것이다.

circle01_black.gifD3DXMATRIX* GetBoxTransform(D3DXMATRIX *pMatCBoxpBox)
pBox
 transform D3DXMATRIX 형태로 변환시켜 주는 일종의 wrapper 함수이다.

circle01_black.gifvoid SetBoxTransform(const D3DXMATRIX* pMatCBoxpBox)
위 함수와 쌍을 이루는이번에는 pMat transform pBox axis 벡터로 변환을 해 주는 함수이다.

circle01_black.gifvoid initBox(CBox *pBox, const D3DXVECTOR3& vecMin, const D3DXVECTOR3& vecMax)
Mesh
로부터 구해 온 bounding box minimum 좌표, maximum 좌표를 받아서 pBox center extent를 계산하고 axis를 초기화한다.

circle01_black.gifvoid moveBox(CBox *pBox, const D3DXMATRIX& mat)
pBox
 mat에 의해 움직인다일단 3개의 axis들을 변환한 이후에 center를 변화하는 방식으로 되어 있다.

circle01_black.gif이 함수들을 이용해서 코드는 다음 순서로 동작하면 된다:

  1. Mesh를 로드 한 후에 D3DXComputeBoundingBox를 이용하여 bounding box를 계산한다.
  2. Bounding box로부터 얻은 값을 이용하여 initBox CBox 객체를 초기화한다.
  3. 이후에 문제가 움직일 때마다 변환 matrix를 인자로 moveBox를 호출해 CBox 객체를 갱신해 준다.
  4. 움직일 때마다 그 CBox와 다른 CBox들이 충돌이 일어났는지 BoxBoxIntersectionTest로 검사한다.

anigray09_up.gif 위로

4. 참고 문헌

5. 웹페이지 다운로드

aniberry01_red.gif웹페이지 전체 다운로드

anigray09_up.gif 위로



반응형

http://irrlicht.sourceforge.net/forum//viewtopic.php?t=43873#p250538


Simple cloud layer SceneNode

Postby tbw » Mon May 16, 2011 6:50 pm

I want to share a litte experiment with you.
I needed a shifting cloud layer in my scene and decided to encapsulate it in a scene node. Here is the result (source code and example link at the bottom of the post)




The CloudSceneNode is a dome that is spanned between the skybox and the scene




The interface of the SceneNode offers access to the geometry and the color of the SceneNode
  // returns the inner radius
                virtual f32 getInnerRadius() { return InnerRadius; }
                // returns the outer radius
                virtual f32 getOuterRadius() { return OuterRadius; }
                
                // returns the center height
                virtual f32 getCenterHeight() { return CenterHeight; }
                // returns the inner height
                virtual f32 getInnerHeight() { return InnerHeight; }
                // returns the outer height
                virtual f32 getOuterHeight() { return OuterHeight; }
 
                // returns the center color
                virtual video::SColor& getCenterColor() { return CenterColor; }
                // returns the inner color
                virtual video::SColor& getInnerColor() { return InnerColor; }
                // returns the outer color
                virtual video::SColor& getOuterColor() { return OuterColor; }
                
                // sets the cloud radius
                virtual void setCloudRadius(
                        f32 innerRadius, 
                        f32 outerRadius);
                
                // sets the cloud height
                virtual void setCloudHeight(
                        f32 centerHeight, 
                        f32 innerHeight, 
                        f32 outerHeight);
 
                // sets the cloud colors
                virtual void setCloudColor(
                        const video::SColor& centerColor = video::SColor(220,220,220,220), 
                        const video::SColor& innerColor = video::SColor(180,180,180,180),
                        const video::SColor& outerColor = video::SColor(0,0,0,0));


The integration into the scene is done as usual:
  // add 1st cloud layer
        cloudLayer1 = new scene::CCloudSceneNode(smgr->getRootSceneNode(), smgr);
        // set translation (speed of the clouds)
        cloudLayer1->setTranslation(core::vector2d<f32>(0.008f, 0.0f));
        cloudLayer1->getMaterial(0).setTexture(0, driver->getTexture("media/clouds/cloud01.png"));
        // set the geometry     
        cloudLayer1->setCloudHeight(0.5f, 0.1f, -0.05f);

For further details please have a look into the source code.
Here is a small example with the source included (build with a 1.8 svn irrlicht version) with three cloudlayers. You can turn them on or off by pressing the keys 1, 2 or 3.

http://www.van-helsing-band.de/irrlicht ... nenode.zip

I hope you like it
Last edited by tbw on Fri Jan 13, 2012 7:35 pm, edited 2 times in total.
tbw
 
Posts: 58
Joined: Sat Jan 15, 2011 9:51 am
Location: Germany

Postby Virror » Mon May 16, 2011 7:38 pm

One word: AMAZING! 
Looks so good i think i will cry : p 
Btw, is this free to use?
Virror
 
Posts: 191
Joined: Mon May 02, 2011 3:15 pm

Postby Luben » Mon May 16, 2011 7:42 pm

It looks very good, thanks for sharing :)
If you don't have anything nice to say, don't say anything at all.
User avatar
Luben
 
Posts: 568
Joined: Sun Oct 09, 2005 10:12 am
Location: #irrlicht @freenode

Postby tbw » Mon May 16, 2011 8:05 pm

Sorry I forgot: license is zlib. So have fun with it! :D
tbw
 
Posts: 58
Joined: Sat Jan 15, 2011 9:51 am
Location: Germany

Postby Virror » Mon May 16, 2011 8:07 pm

Then i will try it out in my game right now : )
Virror
 
Posts: 191
Joined: Mon May 02, 2011 3:15 pm

Postby shadowslair » Mon May 16, 2011 8:32 pm

Looks good. Thanks. :wink:
Image
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
User avatar
shadowslair
 
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

awesome

Postby tecan » Tue May 17, 2011 5:06 am

amazing demo, thanks much!
Live long and phosphor! 
--Luna - Status 60%
User avatar
tecan
 
Posts: 277
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada

Postby hendu » Tue May 17, 2011 7:14 am

That looks excellent :D 

Note it doesn't build as-is: 
In file included from CloudSceneNode.cpp:20:
CloudSceneNode.h:132:7: warning: no newline at end of file
In file included from main.cpp:7:
CloudSceneNode.h:132:7: warning: no newline at end of file
main.cpp: In function 'int main()':
main.cpp:91: error: no matching function for call to 'irr::scene::CCloudSceneNode::setTranslation(irr::core::vector2d<float>)'
CloudSceneNode.h:85: note: candidates are: virtual void irr::scene::CCloudSceneNode::setTranslation(irr::core::vector2d<float>&)
main.cpp:97: error: no matching function for call to 'irr::scene::CCloudSceneNode::setTranslation(irr::core::vector2d<float>)'
CloudSceneNode.h:85: note: candidates are: virtual void irr::scene::CCloudSceneNode::setTranslation(irr::core::vector2d<float>&)
main.cpp:104: error: no matching function for call to 'irr::scene::CCloudSceneNode::setTranslation(irr::core::vector2d<float>)'
CloudSceneNode.h:85: note: candidates are: virtual void irr::scene::CCloudSceneNode::setTranslation(irr::core::vector2d<float>&)


And the skybox isn't loaded, as they are named .PNG, while you call .png. 

But after fixing those, it does look very nice ;) 


(WTF is VC++ doing with a 650kb exe, it's 46kb here :P)
hendu
 
Posts: 1414
Joined: Sat Dec 18, 2010 12:53 pm

Postby tbw » Tue May 17, 2011 8:20 am

Thank you for the hint! 
As you mentioned I compiled the code on a windows machine with visual studio 2008. The compiler doesn't care about the newline at the end of the header files and the file extension is resolved correctly, but only on windows. 
Looks like I've been a little bit lazy :oops: 

So I will fix the code and reupload the sample! 

EDIT: 

Fixed the issues and updated the download
Last edited by tbw on Tue May 17, 2011 6:28 pm, edited 1 time in total.
tbw
 
Posts: 58
Joined: Sat Jan 15, 2011 9:51 am
Location: Germany

Postby REDDemon » Tue May 17, 2011 8:24 am

Very good work. I will add it immediatly to the snippets folder.
OpenGL is not hard. What you have to do is just explained in specifications. What is hard is dealing with poor OpenGL implementations.
User avatar
REDDemon
 
Posts: 827
Joined: Tue Aug 31, 2010 8:06 pm
Location: Genova (Italy)

Postby Buck1000 » Tue May 17, 2011 1:49 pm

That looked AWESOME. Good job :D 

Is there any way you could randomly generate the cloud layer textures at runtime?
User avatar
Buck1000
 
Posts: 93
Joined: Sun Dec 14, 2008 8:02 pm
Location: Seattle, WA

Postby Murloc992 » Tue May 17, 2011 3:18 pm

Buck1000 wrote:That looked AWESOME. Good job :D 

Is there any way you could randomly generate the cloud layer textures at runtime?

LibNoise is there to do exactly what you want. :P
User avatar
Murloc992
 
Posts: 272
Joined: Mon Apr 13, 2009 2:45 pm
Location: Utena,Lithuania

Postby greenya » Tue May 17, 2011 3:50 pm

Just wow! 
This looks really really good :!:
User avatar
greenya
 
Posts: 886
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine

Postby serengeor » Tue May 17, 2011 4:01 pm

I was watching this thread for a few days and after all those replies I got tempted to try it out. 
Simply astonishing :wink: 
This could make many games look more pretty/dynamic :)
Working on game: Marrbles (Currently stopped).
User avatar
serengeor
 
Posts: 1676
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

Postby Tannz0rz » Tue May 17, 2011 11:27 pm

How nice! Runs quite fluently, might I add.

반응형





-1< ( (2-(8*c^2*t^2)/d^2 +u*t ) )/(u*t+2) <1, t>0 , c>0 , d > 0 for t




BLOG main image




반응형
BLOG main image



d^2 으로 계산하나 2*d^2 으로 계산하나 결과는 유사한데


 2*d^2 의 파동이 좀 더 빨리 줄어들게된다


....



float v=D3DXVec2Length(&_waveInfo._velocity);

float dragF=dampingForce;

float ut=dragF*_waveInfo._curTime;

float dd=distance*distance;

float tt=time*time;


float denominator = (ut+2.0f);




.......


//#define _DD 1


#if _DD

//d^2

maxVelocity=(_waveInfo._distance/(2.0f*_waveInfo._curTime))*sqrt(denominator) ;

if( v >= maxVelocity)

v=maxVelocity-0.01;

#else

//2d^2

maxVelocity=sqrt( (2.0f*dd*ut+2)/tt )/2.0f;

if( v >= maxVelocity)

v=maxVelocity-0.01;

#endif

vv=v*v;

float c1=0.0f;

float c2=0.0f;

float c3=0.0f;


#if _DD

//d^2

c1= (4.0f - 8.0f*vv*tt/dd)/denominator;

c3= (2.0f*vv*tt/dd)/denominator;

#else

//2d^2

c1= (4.0f - 8.0f*vv*tt/(2.0f*dd))/denominator;

c3= (2.0f*vv*tt/(2.0f*dd))/denominator;

#endif


c2= (ut-2.0f)/denominator;




.....





#if _DD

//d^2

maxTime= (dragF +sqrt( dragdragF + 32.0f*vv/dd  ) )/(8.0f*vv/dd);

#else

//2d^2

maxTime= (vv*sqrt(dd*(16.0f*vv+dd*dragdragF)/(vv*vv)) + dd*dragF)/(4.0f*vv);


#endif

if( maxTime> _waveInfo._curTime+tick )

_waveInfo._curTime+=tick;




반응형

BLOG main image




2*d^2 에 대해서 푼 경계식



t에 대해서


-1< ( (2-(8*c^2*t^2)/(2*d^2) +u*t ) )/(u*t+2) <1, t>0 , c>0 , d > 0 for t










c에 대해서



-1<  (2-8*c^2*t^2/(2*d^2) + u*t)/(u*t+2)  < 1 , u>0 , t>0 , c>0 , d > 0 for c






반응형


BLOG main image   

http://3dmpengines.tistory.com






 왼손(DX)의 경우  V*A(=행기준 행렬)=V'




이러한 순서가 되며





오른손 (Opengl)의 경우는  A(=열기준 행렬)*V=V'





의 순서가 됩니다


이해하기 쉬우려면  벡터 V 에 대한 좌표변환(회전 또는 위치) 의 각 성분들 중 공통인것끼리만 벡터 V와 곱하면 되는데



왼손의 경우 V가 행렬 A에 의해 변환된다고 할때 =V'의 x 성분은 


각 A축의 x성분, B축의 x성분 C축의 x성분 그리고 pos위치의 x 성분과 각 값을 곱하면 그것이 V'의 x성분이 됩니다


Vx*Ax + Vy*Bx + Vz*Cx + 1*posx = V'x

나머지 V'y, V'z, V'w 성분도 동일합니다



오른손도 마찬가지입니다





그런데 카메라 행렬에서는 이것이 방법과는 약간 미묘한 차이를 보입니다


즉 이동부분과 회전부분에 대해서 각가 다른것을 볼 수 있는데요


회전부분은 월드 위치를 반대로 회전시켜야 하기때문에 A의 회전부분 3x3 부분이 전치가 되어있는 것이고


이동또한 카메라에서 월드세계를 볼때 월드세계의 오브젝트(정점)들이 반대로 이동되게 되면 현실세계에서의


카메라 이동과 동일함으로 이동부분에 - 가 붙게 됩니다




카메라의 경우는 그렇다 치고 일반적인 월드공간 변환과 유사하게


정규직교기저인 벡터 3개만 있다면? 


그렇슴돠~~!!!! 자신이 원하는 공간으로 변환할 수 있게 되는 것이죠!!!




p.s 기초적인것이지만 이 활용도는 생각외로 큽니다




반응형

http://blog.naver.com/oranke?Redirect=Log&logNo=40000788158  에서 일부 내용을 추려옵니다



뷰변환 행렬에 대해 살펴보기 전에 짚고 넘어가야 할 문제가 있습니다. OpenGL과 D3D를 말할 때 가장 큰 차이점으로 오른손 좌표계냐 왼 손 좌표계냐를 따집니다. 그리고 또 하나 얘기하는 것이 바로 행기준 행렬이냐 열기준 행렬이냐는 것이죠...

 

 좌표계 문제는 Z좌표를 뒤집기만 하면 끝납니다. 앞에서 살펴봤던 회전행렬 역시 오른손 좌표계가 오른손 법칙으로 양의 방향을 규정하는 것 처럼 왼손 좌표계는 왼손이 감기는 방향을 양의 방향으로 하기 때문에 회전행렬을 그대로 쓸 수 있습니다. 양의 방향에 해당하는 좌표평면과 축을 2차원에 설정해 그려보면 금방 알 수 있습죠...






OpenGL에서 사용하는 열기준 행렬과 D3D의 행기준 행렬이란 녀석들은 확실하게 그 차이를 이해해 두지 않으면 두고 두고 뒤통수를 맞게 됩니다. 

 참고로 양키들은 행기준 행렬(Row major matrix) 의 곱셈을 프리멀티플리케이션(Pre-multiplication), 열기준 행렬(Column major matrix) 의 곱셈을 포스트멀티플리케이션(Post-multiplication) 이라고 구분해서 쓰더군요.

 

 아무튼 우리가 일반적으로 사용하는 행기준 행렬은 아래처럼 정의되고,

 

  A(Row) = | a00 a01 a02 a03 |
           | a04 a05 a06 a07 |
           | a08 a09 a10 a11 |
           | a12 a13 a14 a15 |

 

 OpenGL 레퍼런스에 따르면 열기준 행렬은 다음과 같이 생겨먹었습니다.

 

  A(Col) = | a00 a04 a08 a12 |
           | a01 a05 a09 a13 |
           | a02 a06 a10 a14 |
           | a03 a07 a11 a15 |





행기준 변환행렬 A에 의해 절점 P(x,y,z,w) 가 새로운 절점 P'(x',y',z',w') 로 변환하는 과정을 살펴보죠. 고등학교 때 배웠듯이 행렬의 곱셈이 성립하려면 행과 열의 갯수가 같아야 하므로 다음과 같이 적어줍니다. (행렬의 어떤 요소끼리 곱하는지 손가락으로 짚으면서 보세요.)

 

  | x' y' z' w' | =

 

  | x  y  z  w  | × | a00 a01 a02 a03 |
                     | a04 a05 a06 a07 |
                     | a08 a09 a10 a11 |
                     | a12 a13 a14 a15 |

 

  x' = x*a00 + y*a04 + z*a08 + w*a12
  y' = x*a01 + y*a05 + z*a09 + w*a13
  z' = x*a02 + y*a06 + z*a10 + w*a14
  w' = x*a03 + y*a07 + z*a11 + w*a15

 

 원래 열기준 행렬은 절점, 또는 벡터를 표현하는 행렬을 세로, 즉 열로 표현하기 위해 사용합니다. 벡터 V(x,y,z,w) 에 변환행렬이 적용된 새로운 벡터 V'를 계산하는 과정을 열기준 행렬로 표현할 때는 다음과 같이 적어 줍니다.

 

  | x' |   | a00 a04 a08 a12 |    | x |
  | y' | = | a01 a05 a09 a13 | × | y |
  | z' |   | a02 a06 a10 a14 |    | z |
  | w' |   | a03 a07 a11 a15 |    | w |

 

 풀어보면 다음과 같습죠. (손가락을 짚으면서 보세요.)

 

  x' = a00*x + a04*y + a08*z + a12*w
  y' = a01*x + a05*y + a09*z + a13*w
  z' = a02*x + a06*y + a10*z + a14*w
  w' = a03*x + a07*y + a11*z + a15*w ... ①

 

 비교해 보나 마나 같은 결과 입니다. 



 OpenGL에서는 ① 에서의 방법으로 행렬을 표시하고 곱해주네요



반응형

http://www.gpgstudy.com/forum/viewtopic.php?topic=7585


Backface Culling에 대한 설명이 좀 부족한듯 싶어서, 설명을 추가해봅니다. 


후면 추리기를 할때 삼각형의 앞면이 어느 방향인지를 알아야 합니다. 


삼각형이 V0, V1, V2 3개의 정점으로 이루어 져있을때, 


삼각형의 법선 벡터를 일반적으로 (V1 - V0) X (V2 - V1) 이렇게 구합니다. 


이 삼각형의 법선 벡터와 카메라 벡터의 사이각이 


90보다 클때 삼각형이 보인다고 판단합니다. (내적의 값이 0보다 작을때) 


그런데 외적의 결과가 왼손 좌표계와 오른손 좌표계에서 서로 반대입니다. 

(Direct3D는 왼손 좌표게를 OpenGL은 오른손 좌표계를 씁니다.) 


삼각형의 법선 벡터가 카메라 벡터와 반대방향이 되면 


왼손 좌표게에서는 카메라에서 봤을때 정점들의 순서가 시계방향이 되고, 


오른손 좌표계에서는 정점들의 순서가 반시계방향이 됩니다. 



반응형

http://mgun.tistory.com/398



이전의 오일러랑 같은역활을 하긴 하지만 
오일러의 단점을 보완하는 쿼터니온이 상당히 중요하다.
오일러의 단점으로 보면
각 축에 대한 회전의 적용순서가 다양하여 순서가 다르면 결과가 달라져 혼란을 야기한다.
- 회전자유도가 3이지만 첫번째 회전축과 세번째 회전축이 거의 일치될 때 회전 자유도가
  하나 감소하는 짐벌락 현상이 발생한다. 
- 회전을 오일러 각도로 표시하면 각 회전에 대한 오일러 각도가 유일하지 않다.
 - 두 오일러각도의 보간이 유일하지 않다.(보간경로의 문제)

이러한 단점을 보완하기 위해 쿼터니온을 사용한다.
사원수는 3차원에 적용하여 공간에서의 방향을 표현한다.

사원수의 장점
- 한 사원수는 하나의 4차원 벡터이고 3차원 공간에서의 한 방향을 표시
- 사원수는 단위벡터
사원수를 행렬로 또는 행렬을 사원수로 쉽게 바꿀 수 있다.
두 사원수를 곱하는 것이 행렬을 곱하는 것보다 더 빠르다
- 두 임의의 방향에 대한 선형보간 또는 구형선형보간이 쉽게 가능하다.

선형보간 
두 벡터들을 선형식으로 보간하여 현재 위치에서 벡터들을 계산.
- 두 점 사이의 최단 직선을 따라 동일한 속도로 이동하는 형태
- 두 점이 사원수라면 단위초구상에 존재, 단위 초구상을 따라 움직이는 두사원수의 최단경로는 
   단위 초구상에서의 두점을 잇는 원호가 된다.

구형선형보간
원호상에서의 등간격 보간.
초구상에는 동일한 회전에 해당하는 사원수가 2개씩 있어 보간시 경로가 가까운 쪽으로의 slerp를 선택하면 된다.
- 짧은경로를 선택하는 방법은 두 벡터를 내적하여 양수면 90도 미만이므로 짧은경로인 것을 알 수 있다.

선형보간과 구형선형보간의 차이
- 한 사원수에서 다른 사원수로 부드럽게 바꾸기 위해 t를 0에서 1로 바꾸면서 보간한다.
  선형보간은 두 사원수를 잇는 직선을 일정 간격으로 진행하면서 사원수들을 구하게 된다.
  구형선형보간은 두 사원수를 잇는 원호를 일정간격으로 진행하면서 사원수들을 구한다.
  그래서 선형보간일 경우 직선상에서는 일정간격이지만 원호상에서는 움직임이 동일간격이 아니며,
  두 점의 중간지점에 가까울수록 이동하는 속도가 빨라지지만 구형선형보간은 이러한 일이 없이 일정한 속도
로 움직인다.

사원수 관련 함수들
D3DXQuaternionIdentity : 단위사원수 만들기 (0,0,0,1)
D3DXQuaternionConjugate : 켤레사원수 만들기
D3DXQuaternionNormalize : 입력사원수를 단위사원수로 만들어 리턴
D3DXQuaternionInverse : 역사원수 만듬(켤레사원수를 정규화한 결과)
D3DXQuaternionMultiply : 두 사원수의 곱
D3DXQuaternionSlerp : 두 사원수를 Slerp 보간
D3DXQuaternionRotationAxis : 벡터 v를 회전으로 하여 사원수 q를 각도 angle만큼 회전.
                                           이 회전에 해당하는 사원수를 구할려면 q를 단위사원수(0,0,0,1)로 지정.
D3DXQuaternionRotationMatrix : 행렬 m에 해당하는 사원수를 만들어 리턴
D3DXMatrixRotationQuaternion : 사원수 q에 해당하는 회전행렬을 만들어 행렬 m 리턴
D3DXQuaternionRotationYawPitchRoll : yaw, pitch, roll로 사원수를 만들어 리턴

뷰행렬
void CameraQuaternion::GetViewMatrix(D3DXMATRIX* vmat)
{
 // 뷰행렬
 if(IsUpdate_)
 {
  // 1. 이동 - 카메라를 원점으로 이동 - 회전방향이 반대가 되도록 켤레사원수를 사용
  D3DXMATRIX matTranslation;
  D3DXMatrixTranslation(&matTranslation, -pos_.x, -pos_.y, -pos_.z);

  // 2. 회전 - 카메라 방향이 월드좌표계의 좌표축에 일치하도록 회전.
  D3DXMATRIX matRotation;
  D3DXMatrixRotationQuaternion(&matRotation, 
   &D3DXQUATERNION(-orientation_.x, -orientation_.y, -orientation_.z, orientation_.w));

  // 3. 회전과 이동을 적용
  D3DXMatrixMultiply(&view_, &matTranslation, &matRotation);
  IsUpdate_ = false;
 }

 (*vmat) = view_;
}

이동
void CameraQuaternion::Walk(float fUnits)
{
 // z축
 D3DXVECTOR3 look;
 look.x = view_._13;
 look.y = view_._23;
 look.z = view_._33;

 if(ECT_WALKER == cameraType_)
  look.y = 0.0f;

 pos_ += look * fUnits * moveSpeed_;
 IsUpdate_ = true;
}

위치
void CameraQuaternion::Pitch(float fAngle)
{
 fAngle *= (rotateSpeed_ / 60);

 // right 방향으로 회전
 D3DXQUATERNION rot;

 // 사원수로 right 벡터를 구해서 주어진 각도만큼 right 벡터방향으로 회전하는 사원수 rot를 구한다.
 D3DXQuaternionRotationAxis(&rot, TransformAxis(&orientation_, &D3DXVECTOR3(1.0f, 0.0f, 0.0f)), fAngle);

 orientation_ *= rot; // 카메라 방향인 orientation 사원수에 rot 사원수를 곱하여 회전효과를 만든다.

 D3DXQuaternionNormalize(&orientation_, &orientation_); // 사원수 정규화.
 IsUpdate_ = true;
}

보간
bool CameraQuaternion::Slerp(D3DXQUATERNION* quatTarget)
{
 // 두 방향 사이를 부드럽게 보간(sphere linear interpolation)
 if(orientation_ == * quatTarget)
  return false;

 // 두 사원수 orientation과 quatTarget을 slerpSpeed(0~1)사이의 값으로 Slerp 보간.
 D3DXQuaternionSlerp(&orientation_, &orientation_, quatTarget, slerpSpeed_);
 D3DXQuaternionNormalize(&orientation_, &orientation_);
 IsUpdate_ = true;
 return true;
}

fx는 동일하다.



반응형

저번에서 얘기 했듯이 이번에는 복소평면에서의 복소수의 성질에 대해 알아보자

 


 
 

 

위에 나오는 평면을 복소평면이라 하는데, 복소평면이란 가로축을 실수축, 세로축을 허수축으로하는 평면을 말한다.

 

 

처음에는 항상 그랫다 싶이 사람들은 허수를 믿지 않았다.  왜냐면 볼 수 없었기 때문이다. 실제로 사람들이 음수를 인정하지 않던 이유도 '-3개의 사과','-5ㅡm의 막대'를 볼 수 없었기 때문이었다. 그래서 덴마크의 측량기사 카스파르 베셀(1745~1818)은

이렇게 생각했다.

"허수는 수직선의 어디에도 없다. 그렇다면 수직선의 밖, 즉 원점에서 위의 방향으로 뻗은 화살표를 허수로 생각하면 되지않을까?"

이 방법을 이용하면 드디어 허수가 "눈에 보이게" 된다.

 

이러한 아이디어는 비슷한 시기에 프랑스의 회계사 장 로베르 아르강(1768~1822)와 독일의 수학자 카를 프리드리히 가우스 (1777~1855)에 의해서도 발표되었다. 그래서 복소평면을 가우스 평면이라고도 한다.

 

설명은 이정도로하고 복소수의 기하학적의미에 대해 파악해보도록 하자.

 

일단 복소수의 사칙연산이다.

 


 위의 그림은 복소수의 덧셈을 표기한 것이다. 고2학생들중 기학벡터를 배운 학생 또는 그 이상의 교육을 받은 사람이면 어디서 많이 본듯한 그림일 것 같다. 복소수의 덧셈은 마치 벡터의 합을 구하는 것과 매우 흡사하다.

 

합을 눈으로 봤으니 이제는 곱을 눈으로 봐보자.

곱을 보기 전에 일단 허수단위 i의 순환을 눈으로 보고 곱으로 들어가도록 하자.

  • i4n − 3 = i
  • i4n − 2 = − 1
  • i4n − 1 = − i
  • i4n = 1 (이상, n은 정수)

위의 순환 성질과 밑의 그림의 관련성이 보이는가?

허수에 대해 잘 배운 사람이면 보일지도 모르겠다. 허수의 곱셈은 회전의 의미 가 있다.

위의 경우에는 i를 곱함으로써 점이 90도 회전하는 걸 볼 수 있다.

 

이를 다른 일반적인 복소수에 대해서도 일반화를 시키자면

위에서 보면 복소수 a+bi 와 c+di 를 곱하고 있다. 이것을 잘 한 번 살펴보자

 

위에서 과 는 각각의 복소수의 절댓값으로서 원점과의 거리를 의미하고 과 는 실수축의 양의방향과 이루는 각도이다.

 

이제 (a+bi)(c+di) 를 해보자

 


 

 

위 두식을 비교해 보면 알 수 있듯이 두 복소수의 곱의 절댓값은 두 절댓값의 곱과 같다는 것을 알 수 있다.

이번에는  각의 관점에서 복소수의  곱을 봐보자


 
 두 복소수를 위와 같이 놓고 곱을 해보자

 


 
 위 식으로 알 수 있듯이 두 복소수의 곱이 이루는 각  복소수의 각을 더한 것과 같아진다.

(만약 위식이 이해가 가지 않으면 삼각함수의 덧셈정리라고 검색을 한번 해보시길)

 

이런식으로 복소수의 곱을 해보면 쉽게 회전 확대의 원리를 이해 할 수 있을 것이다.

 

오늘은 여기 까지만 하고 다음번 복소수에서는 이런 복소수가 도데체 어디에 쓰이는 지 한번 알아보도록 하자.

[출처] 복소수, 허수에 대해 2|작성자 jini

반응형

pdf 자료

반응형

http://www.gpgstudy.com/m/forum/topic/13306
렌더링 하는 부분에서 Apply()를 호출해줘서 자동으로 행동을 제어하려고 합니다. 

그러나 위에서 말씀드렸듯이 Yaw를 사용하지 않으니 목표로 비행기답게 이동하게 하기 위해서 대체 어떤 방식을 해야 할지 막막합니다. 중간에 Accelate() 부분에서 적군 비행기를 가속도 시키면서 Roll과 Pitch를 조정하려고 하는데.. 

조언 좀 부탁드릴께요. 막막하네요..;

음 일단..

음.. 목표의 벡터와 현재 기체의 벡터의 외적으로 나온 벡터로 축회전을 해주고, 그 회전에 맞춰 전방, 상향, 우향 벡터도 회전시켜줬습니다. 이렇게 하니 가라성은 있지만 그나마 비행기 답게 목표를 향해 움직이긴 하네요.. 좀 더 나은 방법은 없을까요? ㅜㅜ

Steering Behavior 를 사용해심이 어떠신지...

Steering Behavior 

비행기나 자동차 제어 등에 쓸만한 방식입니다. 
목적지까지 이동이라면 Arrival을 쓰시면 되겠구요 
 

물체 추적이라면 seek 혹은 pursuit을 쓰시면 되겠습니다. 
 
 

실용적 예제로 본 게임인공지능 프로그램하기 란 책에도 잘 설명되어 있습니다.

추가로...

Autonomous Behaviors for Interactive Vehicle Animations라는 논문도 있습니다. 
약간 다른 방식이긴 하지만 이것도 좋은 참고거리가 되실거라보입니다.

Re:

올로님이 올려주신 사이트.. 멋지네요. 
찾아보니 C++ 라이브러리도 만들어 놨군요. 

마침 제가 여기 딱 맞아떨어지는 프로젝트를 하고 있긴 한데 
저는 임기응변식으로 인공지능 짜는 일에 더 능숙한 관계로.. 공부는 언제나 skip skip~

반응형

블로그 이미지

3DMP engines

3D그래픽스 물리 수학, 프로그래밍 GPU Shader 게임엔진 알고리즘 디자인패턴 matlab etc.







회전사원수의 곱을 행렬로 만들다보면 X*Y*Z 또는 Z*Y*X 오일러 회전행렬을 곱한 순서가아니라는 것을 알수있다

회전사원수의 곱은 Z*X*Y 행렬 곱으로 나타난다





[왼손기준 = Z*X*Y 행렬 ]

{{cosz,sinz,0},{-sinz,cosz,0},{0,0,1}}*{{1,0,0},{0,cosx,sinx},{0,-sinx,cosx}}*{{cosy,0,-siny},{0,1,0},{siny,0,cosy}}

[오른손 기준 = Z*X*Y 행렬 ]




{{cosy,0,siny},{0,1,0},{-siny,0,cosy}}*{{1,0,0},{0,cosx,-sinx},{0,sinx,cosx}}*{{cosz,-sinz,0},{sinz,cosz,0},{0,0,1}}

반응형

블로그 이미지

3DMP engines

3D그래픽스 물리 수학, 프로그래밍 GPU Shader 게임엔진 알고리즘 디자인패턴 matlab etc.





구면선형보간함수를 구현하다보면 일반적인 벡터부분만 뽑아와 내적을 하는것이  DirectX 에서 제공하는 함수와 결과가

한번씩 반대로 나온다는것을 알 수 있다


그것을 아래 첨부한 내용과 같이 해결하면 된다






http://www.gpgstudy.com/forum/viewtopic.php?t=1949


내적이 0보다 작은 경우에 두 쿼터니온 중 하나의 부호와 내적결과의 부호를 반대로 해야한다

D3DX 의 slerp 함수는 이처럼 되어있음






반응형

http://www.gpgstudy.com/forum/viewtopic.php?p=121140

올리기

Re: D3DXMatrixTranspose용도는?

올려짐: 2009-06-26 01:54
인용과 함께 답변 이 게시물을 del.icio.us에 추가


비회원 씀:
D3DXMatrixTranspose이건 어느 상화에 쓰는건가요?

행과 열을 서로 바꾸는 걸 하나 본데 말이죠

그리고
d3dxmatrixinverse(&m,NULL,&mworld);
이러면 world좌표가 local좌표로 바뀌는건가요?

그럼 감사합니다



openGL의 경우 오른손 좌표계고 DirectX의 경우 왼손 좌표계 입니다.

openGL을 쓰다가 D3DX수학함수들이 쓰고 싶을때.. D3DX 함수로 계산한 행렬을 openGL에 적용시킬때는
D3DXMatrixTranspose해서 넘겨줘야겠죠.

그리고 쉐이더에서 전치행렬이 필요한경우가 있습니다. 이때도 Transpose를 쓰죠.

반응형

http://www.gpgstudy.com/forum/viewtopic.php?p=100599

왼손좌표계와 오른손좌표계 때문에 행렬이 전치관계를 갖는 것이 아니라
row-major이냐 column-major이냐에 따라 행렬이 전치관계를 갖고 행렬 곱셈 순서도 바뀝니다.

DirectX는 row-major이고 OpenGL은 column-major이니까 행렬도 전치해주고 곱셈 순서도 바뀌어야 합니다.

행렬의 row-major/column-major는 왼손좌표계/오른손좌표계와 독립적입니다.
예를 들어, nebula엔진이 row-major(DirectX)를 쓰고 오른손좌표계(OpenGL)을 사용하는 것으로 알고 있습니다.

반응형

'수학 (Mathematics) > 3D수학' 카테고리의 다른 글

구면선형보간의 핵심요소  (0) 2012.11.02
d3dx 수학 함수들을 opengl 에서 쓰고 싶을때  (0) 2012.11.02
짐벌락 피하는 방법  (0) 2012.11.02
쿼터니언  (0) 2012.11.02
광선추적 (Ray Tracing)  (0) 2012.11.02

[게임수학] 짐벌락과 쿼터니언 - 1 게임수학 / 개인

2012/08/30 13:15

복사http://blog.naver.com/qkfkf123/60170135935

짐벌락과 쿼터니언

- 짐벌락

짐벌락이란?

= x축(Pitch) y축(Yaw) z축(Roll) 의 세축이 회전을 하다가

특정 축이 특정각으로 회전을 했을 때 두 축이 겹쳐서 한축이 소실되는 현상을 말한다.

짐벌락 현상이 일어나는 이유!

1. 오일러 각을 이용하기 때문에

2. 오일러 각 회전을 순차적으로 하기 때문에

짐벌락 현상을 완벽하게 막을 방법?

- 단정적으로 말하자면 존재하지 않다.

즉, 우리는 짐벌락현상을 최대한 피하는 방법 밖에 없다.

짐벌락 현상을 최대한 피하는 방법

1. 오일러 각 회전의 순서를 바꾼다.

2. Axis라는 임의의 축회전을 이용한다.

3. 쿼터니언을 이용한다.

오일러 각 회전의 순서 바꾸기

- 잘 생각해보면 xyz 축회전에서 순서를 바꾸어 나타낼 수 있는 가지수는 총 6가지이다.

즉, xyz xzy yxz yzx zxy zyx 로 회전의 순서를 바꿀 수 있다.

여기서 잠깐! 순서를 정하는 방법을 알아보자.

1. 가장 자주 회전하게 될 축을 먼저 회전한다.

2. 가장 회전을 안하게 될 축을 2번째로 회전한다.

3. 남은 축을 회전한다.

이유!? 순서에 의한 짐벌락 현상은 두번쨰 회전하는 축이 90도(-90도)를 회전하게 되면

1번째 축과 3번째 축이 겹치면서 발생한다.

즉, 순서를 바꿔서 최대한 피하는 방법은 두번째 회전하는 축이 90도가 되지 않으면 된다.

다시 말해 두번째 회전하는 축이 회전을 자주하지 않으면 90도가 될 확률이 낮기에 위와같은 순서로 회전한다.

<영상 자료>

http://serviceapi.nmv.naver.com/flash/convertIframeTag.nhn?vid=3BC022F3654BF874381C994D9E29BBA102C3&outKey=V128a5c729f05a8b0b788bb9ead3d6b3a7a4ada8c814771624487bb9ead3d6b3a7a4a&width=720&height=438

Axis라는 임의의 축회전을 이용하는 방법

- 사실 아직 정보가 부족하다. 우선적으로 알고 있는 정보는

'D3DXMatrixRotationAxis' 라는 함수 이다.

이 함수는 인자로 결과행렬을 내 뱉고, 임의의 축이 될 벡터와 회전을 먹일 회전행렬을 인자로 원한다.

여기까지가 필자가 알고있는 Axis에 대한 정보이다.

하지만 이 임의의 축회전도 문제가 있다.

임의의 축을 놓고 회전할 때 보간의 처리( 선형보간, 구면보간 )의 문제로 회전 시에 뚝뚝 끊겨보이게 된다.

이 문제를 해결할 쿼터니언이 존재한다. ( 물론 쿼터니언도 완벽하게 짐벌락을 막을 수는 없다. )

쿼터니언은 비교적 어려운 수학적 학문이므로 다음 포스트로 옮기도록 하겠다.

다음 포스팅은 쿼터니언에 대해 정리하고

지금 이 포스팅에 이미지, 영상 추가 등

최종본으로 정리하도록 하겠습니다. ㅋ

*

수정 완료 ^^


아래 내용은 위 내용에 조금 추가하면

위의 이미지는 x 축을 회전 시키는 과정이고 아래 그림은 x 축을 90 또는 -90 도로 회전시켰을때 이고

이때 y축과 z 축이 같은 회전 선상에 있다는 것을 알 수 있다

이때 짐벌락이 발생한다

반응형

 


 

출처 : http://www.galexandria.com/

처음에

저는 쿼터니언이라는 용어가 마치 비밀스러운 어둠의 힘을 가지고 있는 암흑물체에 관한 양자론 용어처럼 느껴집니다. 여러분도 역시 이 어둠의 힘에 매료되었다면 이 글이 도움이 될 것입니다(그렇게 되기를 바랍니다). 이 글은 쿼터니언을 이용해서 회전을 하는 방법을 보여주어 쿼터니언을 좀더 쉽게 이해하는데 도움을 줄 것입니다. 만약 여러분이 글에서 잘못된 내용을 발견하시면 robin@cyberversion.com로 메일을 보내주세요. 또, 이 기사를 여러분의 사이트에 실으려고 하신다면 저에게 메일을 보내주세요. 저는 저의 글이 어디에 퍼져있는지 알고 싶답니다.

왜 쿼터니언을 사용할까?

이 질문에 대답하기 위해서 우선 방향을 나타내는 일반적인 방법에 대해 논의해 보도록 합시다.

오일러 표현

이것이 현재까지 알려진 방법중 방향을 나타내는 가장 간단한 방법입니다. 오일러 표현은 각각의 축마다 축주위의 회전량을 가지고 있습니다. 따라서, 다음과 같은 0도에서 360도(혹은 0~2π)의 범위에서 변하는 3개의 값을 가집니다.

x, y, z <-- 전역 좌표축 주위의 회전 각도

이 값은 각각 롤, 피치, 요(혹은 피치, 롤, 요, 등 등)를 표현합니다. 방향은 3개의 각도로부터 생성되는 3개의 회전 행렬을 지정한 순서대로 해서 구할수 있습니다.

Note: 회전은 전역 좌표계의 좌표축을 기준으로 회전합니다. 즉 최초의 회전이 그 후의 2번째 3번째의 회전축에 영향을 주지 않습니다. 이 때문에 짐벌락(gimbal lock) 문제가 발생하게 됩니다. 짐벌락에 대해서는 조금 후에 자세히 설명하겠습니다.

회전축과 각도(Axis Angle)에 의한 표현

이 방법은 짐벌락을 피할수 있으므로 오일러각 방법보다 좀더 낫습니다. 회전축과 각도에 의한 표현은 임의의 회전축을 나타내는 단위 벡터와 단위 벡터 주위의 회전을 나타내는(0~360) 값으로 구성됩니다.

x, y, z <-- 임의의 축을 나타내는 단위 벡터 angle <-- 바로 윗줄에서 정의한 축 주위로 회전 각도

왜 이 방법이 나쁠까요?

짐벌락

오일러 표현에 있어서 회전은 전역 좌표계에서 일어나기 때문에 한 축의 회전이 다른 축의 회전과 겹치는(override) 문제가 발생합니다. 따라서 회전을 할 수 있는 축 하나를 잃게 됩니다. 이것을 짐벌락이라고 합니다.

예를 들어, X축과 평행한 어떤 벡터를 Y축주위로 회전해서 그 벡터가 Z축과 평행하게 되었다고 하면 이 때, Z축주위로 아무리 회전시켜도, 벡터의 방향은 전혀 변하지 않게 됩니다.

나중에 여러분에게 짐벌락의 예와 쿼터니언을 사용해서 짐벌락을 해결하는 방법을 보여드리겠습니다.

보간 문제

회전축 표현이 짐벌락 문제로 부터 자유롭긴 하지만 두개의 회전을 보간하는 경우, 또 다른 문제가 발생합니다. 보간계산을 마친 회전들이 부드럽지 못하여 회전 애니메이션이 삑사리가 날 수도 있습니다. 오일러 표현도 마찬가지로 이 문제를 가지고 있습니다.

자. 쿼터니언으로 빠져 봅시다 !

시작하기에 앞서, 몇가지 가정을 세우겠습니다. 저는 수학적인 이해가 중요시되는 글에서 수학적인 내용들을 대충 생략해버리는 글들에 진절머리가 납니다. 이런 글들은 독자들을 혼란에 빠뜨리기 쉽상입니다.

좌표계 - 이 글은 OpenGL과 같은 오른손 좌표계를 사용합니다. 만약 여러분이 Direct3D 같은 왼손 좌표계를 사용하고 계시다면 행렬들을 전치(transpose)하셔야 합니다. 이미 Direct3D의 샘플들은 쿼터니언 라이브리러를 가지고 있다는 사실에 유념해주시기 바랍니다. 그럼에도 불구하고 저는 여러분이 그 라이브러리를 사용하시기 전에 그 내부가 어떻게 구성되는지를 한번 짚고 넘어갔으면 하는 바램입니다.

회전순서 - 오일러 표현에서 회전 순서는 X->Y->Z의 순입니다. 행렬 형태로 아래와 같이 표현합니다.

RotX * RotY * RotZ <-- 매우 중요

행렬 - 행렬은 OpenGL 처럼 열우선 방식으로 합니다.

예 [ 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15 ]


벡터와 점 - 변환을 위해 벡터와 점은 4x1의 행렬로 표현합니다. 다음과 같은 모습입니다.

회전 행렬 * [ vx vy vz 1 ] <-- 4x1 벡터

저는 특히 Direct3D보다 OpenGL을 선호하지는 않습니다. 그저 제가 OpenGL을 먼저 배웠고, 쿼터니언도 OpenGL을 통해 익혔을 뿐입니다.

Note: 만약 X->Y->Z 순서가 아닌 다른 회전 순서를 지정하면 몇몇 쿼터니언의 함수들을 다시 구현해야 합니다. 특히 오일러 표현을 다루는 함수들이 그렇습니다.

쿼터니언이란 무엇인가?

복소수는 i라는 기호를 사용하여 정의하는 허수(가상의 수)입니다. i는 i * i = -1 라는 성질을 가지고 있습니다.

쿼터니언은 복소수의 확장입니다. i만 사용하는 것이 아니라 제근이 ―1 이 되는 3개의 허수를 가집니다. 이 3개의 수는 보통 i, j, k로 표기합니다. 다시 말해 이것은 다음과 같은 성질을 가집니다.

j * j = -1 k * k = -1

따라서 쿼터니언을 아래와 같이 표현할 수 있습니다.

q = w + xi + yj + zk

여기서 w는 실수, x, y, z는 복소수입니다.

흔히 사용하는 또 다른 표현은 아래처럼 벡터형태로 표현할수 있습니다.

q = [ w, v ]

여기서 v = (x, y, z)는 "벡터"라고 말하며 w는 "스칼라"입니다.

v를 벡터라고 부르지만 이것은 일반적인 3차원 벡터가 아닌 4 차원 공간상에 벡터를 표현한 것으로 직관적으로 시각화할 수 없습니다.

항등 쿼터니언

벡터와 다르게 2개의 항등 쿼터니언이 있습니다.

 항등 쿼터니언은 아래 처럼 표현합니다.

q = [1, (0, 0, 0)]

그래서 이  항등 쿼터니언과 해진 어떤 쿼터니언도 변하지 않습니다.

가산 항등 쿼터니언은 (저희는 사용하지 않습니다)은 아래처럼 표현합니다.

q = [0, (0, 0, 0)]

방향 표현으로 쿼터니언 사용하기

우선 저는 쿼터니언이 벡터가 아니라는 사실을 말씀드리고 싶습니다. 따라서 여기서 벡터 수학을 사용하지 말아주십시요.

이 부분은 매우 수학적인 내용을 다룰 것입니다. 참을성을 가지고 제 설명을 읽어주세요.

우선 쿼터니언의 크기를 정의합니다.

|| q || = Norm(q) = sqrt(w2 + x2 + y2 + z2)

단위 쿼터니언은 아래와 같은 속성을 가지고 있습니다.

w2 + x2 + y2 + z2 = 1

그 때문에 쿼터니언의 정규화는 아래와 같이 구합니다

q = q / || q || = q / sqrt(w2 + x2 + y2 + z2)

이 단위 쿼터니언은 특별합니다. 왜냐하면 단위 쿼터니언은 3D 공간에서 방향을 표현할 수 있기 때문입니다. 따라서 앞에서 논의된 두가지 방법 대신에 단위 쿼터니언을 통해 방향을 표현할 수 있습니다. 단위 쿼터니언으로 방향을 표현하려면 단위 쿼터니언을 다른 표현(예: 행렬)으로 변환하거나 반대로 변환하는 방법이 필요합니다. 이것에 대해서는 곧 설명드리겠습니다.

단위 쿼터니언 시각화 하기

단위 쿼터니언은 (x, y, z) 요소를 임의의 축, w요소를 회전 각도로 하는 4차원 공간상의 회전으로서 시각화할 수 있습니다. 모든 단위 쿼터니언들은 4D 공간에서 단위 길이를 가지는 구를 형성하게 됩니다. 다시한번 이게 무슨 소리지 하고 여러분은 직관적으로 이해가 안되실것입니다. 하지만 제가 정말 말하고 싶은 것은 쿼터니언의 스칼라 요소(w)의 부호를 반전시키는 것만으로 180도 회전한 쿼터니언을 얻을 수 있다는 것입니다.

Note: 단위 쿼터니언만을 방향표현에 사용할 수 있습니다. 이 뒤에 논할 모든 내용은 모두 단위 쿼터니언을 사용한다고 가정합니다.

쿼터니언으로부터의 변환

효과적으로 쿼터니언을 사용하려면 결국 쿼터니언을 다른 표현으로 변환해야 할 필요가 있을 것입니다. 키 눌림을 쿼터니언으로 해석할 수는 없지 않습니까? 할수 있으신 분 계신가요? 글쎄여. 아직까지는 없는 듯 하죠?

쿼터니언에서 행렬로의 변환

OpenGL와 DirectX는 행렬로 회전을 표현하기 때문에 쿼터니언->행렬 변환이 아마 가장 중요한 변환 함수일 것입니다. 왜냐하면 동차 행렬이 3D의 기본 표현이기 떄문입니다.

쿼터니언 회전과 동등한 회전 행렬은 다음과 같습니다.

 

행렬 = [ w2 + x2 - y2 - z2      2xy - 2wz       2xz + 2wy             0

            2xy + 2wz          w2 - x2 + y2 - z2   2yz - 2wx             0

            2xz - 2wy                2yz + 2wx    w2 - x2 - y2 + z2      0

                  0                            0                   0         w2 - x2 - y2 + z2]

 

 

w2 + x2 + y2 + z2 = 1이 되는 단위 쿼터니언의 속성을 사용하면 위 식을 다음과 같이 간단하게 만들 수 있습니다.

 

행렬 = [ 1 - 2y2 - 2z2          2xy - 2wz         2xz + 2wy            0

            2xy + 2wz            1 - 2x2 - 2z2       2yz - 2wx            0

            2xz - 2wy              2yz + 2wx        1 - 2x2 - 2y2         0

                  0                          0                     0                   1 ]

쿼터니언으로부터 회전축과 각도에 의한 표현으로 변환

한 쿼터니언을 삼차원 공간에서의 임의축 주위의 회전축과 각도에 의한 표현으로 변환하는 방법은 다음과 같습니다.

회전축이 (ax, ay, az)이고 각도가 theta (라디안)이면 각도는 angle= 2 * acos(w)가 됩니다. 그 때 ax= x / scale ay= y / scale az= z / scale 이 됩니다. scale은 scale = sqrt (x2 + y2 + z2)입니다..

제가 알고 있는 다른 방법은 scale = sin(acos(w))을 사용하는 것입니다. 저 스스로 수학적으로 동치관계를 증명하려고 하지는 않았지만 두 방법 다 결과는 같을 것이라 생각합니다.

어쨌든, scale이 0이라면 회전이 없다는 뜻입니다. 그리고 여러분이 특별한 조치를 취하지 않는다면 회전축이 무한대가 됩니다. 따라서 scale이 0인 경우에는 언제나 회전각이 0인 임의의 단위벡터를 그 축에 설정하시면 됩니다.

간단한 예

제가 설명하려고 하는 것들에 대해서 뭐가 뭔지 모르겠다 하시는 독자분들을 위해 이제부터 간단한 예를 보여드리겠습니다.

우선 카메라 방향을 오일러 각으로 표현한다고 해봅시다. 그러면 렌더링 루프에서 다음과 같은 식을 이용하여 카메라를 위치시킵니다.

RotateX * RotateY * RotateZ * Translate

이 때 각각의 요소는 4x4 행렬입니다.

이제 단위 쿼터니언을 사용해서 카메라의 방향을 표현하려면 먼저 쿼터니언을 행렬로 변환해야 합니다. 그러면

(쿼터니언으로부터 변환한 회전 행렬) Rotate * Translate

같은 것이 생기게 됩니다

OpenGL에 특화한 예는 다음과 같게 될것입니다.

오일러쿼터니언

glRotatef( angleX, 1, 0, 0)
glRotatef( angleY, 0, 1, 0)
glRotatef( angleZ, 0, 0, 1)
// 평행이동

// 오일러를 쿼터니언으로 변환
// 쿼터니언을 회전축과 각도로 변환
glRotate(theta, ax, ay, az)
// 평행이동

위의 표현은 모두 같습니다. 제가 말할려고 하는 것은 방향에 쿼터니언을 사용하는 것은 오일러나 회전축과 각도에 의한 표현과 완전히 같고, 앞서 언급한 변환 함수를 통해 상호교환이 가능하다라는 것입니다.

다만, 위의 쿼터니언에 의한 표현에는 오일러 표현법과 마찬가지로 짐벌락의 위험성이 있습니다.

역자주 – "왜 짐벌락 위험성이 있지"라는 답을 글내용상 알수가 없습니다.


물론, 아직은 회전을 쿼터니언으로 만드는 방법을 모르시겠지만 그것은 아래 부분에서 설명하겠습니다.

Note: Direct3D나 OpenGL를 사용하시는 독자분들은 API가 행렬 연결을 처리해주기 때문에 직접 행렬을 취급하는 일은 없겠지만, 이것에 대해서 알아둘 필요가 있습니다.

쿼터니언 

단위쿼터니언이 3D공간에서의 한 방향을 표현하기 때문에 2개의 단위 쿼터니언간의 은 2개의 단위 쿼터니언의 회전을 결합한 회전을 나타내는 단위 쿼터니언이 됩니다. 놀라운 일이지만, 사실입니다.

다음과 같은 2개의 쿼터니안이 있다고 가정해봅시다.

Q1=(w1, x1, y1, z1); Q2=(w2, x2, y2, z2);

이 2개의 단위 쿼터니언을 결합한 회전은 아래와 같이 구해집니다

Q1 * Q2 =( w1.w2 - v1.v2, w1.v2 + w2.v1 + v1*v2)

이 때 ,

v1 = (x1, y1, z1) v2 = (x2, y2, z2)

이 됩니다.

여기서 .와 *은 내적과 외적을 나타냅니다.

하지만 위 식을 아래와 같이 최적화할 수 있습니다.

w = w1w2 - x1x2 - y1y2 - z1z2 x = w1x2 + x1w2 + y1z2 - z1y2 y = w1y2 + y1w2 + z1x2 - x1z2 z = w1z2 + z1w2 + x1y2 - y1x2

물론, 여타 쿼터니언들과 마찬가지로 결과 단위 쿼터니언을 다른 회전 표현으로 변환하는 것도 가능합니다. 이런 점이 바로 쿼터니언의 진정한 미학입니다. 2개의 단위 쿼터니언은 구 상에 위치하기 때문에 4차원 공간에서 이들을 하는 방법은 짐벌락의 문제를 해결합니다.

여기서 하는 순서가 중요합니다. 쿼터니언의 은 교환칙이 성립되지 않습니다. 즉 이것은 아래의 식을의미합니다.

q1 * q2 ≠ q2 * q1

Note: 2개의 쿼터니언은 동일한 좌표계 축을 참조해야 합니다. 저는 서로 다른 좌표계에서 2개의 쿼터니언을 합성하는 실수를 범한 적이 있고, 이 때 그 결과 쿼터니언이 특정 각도에서만 실패하는 이유를 알아내기 위해 많은 시간을 고민했습니다.

쿼티니언으로의 변환

이제 다른 표현법을 쿼터니언으로 변환하는 방법을 배워봅시다. 저는 샘플 프로그램에 있는 모든 변환들을 사용하지는 않지만 역운동학 등의 보다 진보된 용도로 쿼터니언 방향을 사용하려고 할 때 이것들이 필요할 것입니다.

회전축과 각도(Angle Axis)로부터 쿼터니언으로 변환

3D공간에서의 임의의 회전축을 도는 회전은 아래처럼 쿼터니언으로 변환됩니다.

회전축이 (ax, ay, az) 이고 (반드시 단위 벡터여야 함) 회전 각도를 theta (라디안)이면 w = cos(theta/2) x = ax * sin(theta/2) y = ay * sin(theta/2) z = az * sin(theta/2)이 됩니다.

우선 회전축이 정규화되어 있어야 합니다. 만약 정규화된 회전축 벡터의 길이가 0이라면 회전이 없는 것을 의미하므로 쿼터니언은 단위(identity) 쿼터니언으로 설정되어야 합니다.

오일러로부터 쿼터니언으로 변환

오일러로부터 쿼터니언으로의 변환은 회전순서가 올바르지 않으면 안 되기 때문에 조금더 힘듭니다. 임의축으로부터 3개의 좌표축을 분해하면 오일러 각을 3개의 독립적인 쿼터니언으로 변환해서 이 3개의 쿼터니언을 하면 최종 쿼터니언을 얻을 수 있습니다.

따라서 오일러 각 (a, b, c)으로 세개의 독립적인 쿼터니언을 만들수 있으며

Qx = [ cos(a/2), (sin(a/2), 0, 0)] Qy = [ cos(b/2), (0, sin(b/2), 0)] Qz = [ cos(c/2), (0, 0, sin(c/2))]

최종 쿼터니언은 Qx * Qy * Qz로 구할수 있습니다.

역자주 –이것은 오일러 회전 순서가 x->y->z의 순서인 경우에만 해당됩니다. 다른 순서로 된다면 Qx * Qy * Qz이 다르게 표현됩니다

데모 - 짐벌락 피하기

드디어 "어떻게 쿼터니언이 짐벌락을 피할수 있지?"에 대해서 모두가 기다렸던 궁금증에 대답할 때가 왔습니다.

기본적인 아이디어는

  1. 회전을 표현하는 데 쿼터니언을 사용한다.
  2. 현재 방향에서 새로운 방향으로 변경하기 위해 하나의 임시 쿼터니언을 생성한다.
  3. 임시 쿼터니언과 원래의 쿼터니언을 한다. 이렇게 하면 양쪽 모두의 회전을 합성한 새로운 방향이 얻어진다.
  4. 이 결과 쿼터니언을 행렬로 변환해서 평소처럼 행렬 을 사용한다.

우선, 저는 샘플 코드에 대해서는 어떠한 책임도 지지 않겠습니다. 이 코드는 난잡스러고 제대로 구성되어 있지도 않습니다. 이것은 쿼터니언 테스트할 때 제 프로그램에서 사용했던 쿼터니언 코드를 간략히 줄여놓은 버전일 뿐입니다. 따라서 코드에 대해서 크게 신경을 쓰지 않았다는 점만 기억해 주셨으면 좋겠습니다(돈받고 파는 코드가 아니니까요 ^^)

저는 2개의 실행 가능한 샘플을 준비했습니다. 첫번째 프로그램인CameraEuler.exe (http://www.gamedev.net/reference/programming/features/qpowers/CameraEuler.zip)는 오일러 각을 사용해서 카메라를 구현한 예입니다.

여러분이 주의깊게 봐야 할 부분은 main.cpp의 Main_Lopp 함수입니다.

여러분이 while 문에서 눈여겨 봐야할 곳은 다음과 같습니다.

  1. X축, Y축, Z축 각각의 회전을, 3개의 각도로 보관 유지 부분
  2. 키를 누르면 관련된 회전량을 조정하는 부분
  3. while 문에서, 3 개의 오일러 각도를 회전 행렬로 변환해서 그것을 최종 변환 행렬에 하는 부분

위/아래 화살표 키는 X축의 회전, 왼쪽/오른쪽 화살표 키는 Y축회전, Insert/PageUp 키는 Z축회전을 담당합니다.

이 프로그램은, 짐벌락을 일으킵니다. 여러분이 짐벌락 현상을 보고 싶으면, 요를 90도로 취하고 X축이나 Z축회전을 시도해 보십시오. 그리고 무슨일이 일어나는지 살펴보세요.

지금부터는 쿼터니언으로 짐벌락 문제를 해결한 프로그램을 살펴봅시다. 이 프로그램은CameraQuat.exe (http://www.gamedev.net/reference/programming/features/qpowers/CameraQuat.zip)이며 앞의 프로그램을 조금 변경한 프로그램입니다.

여러분이 while문에서 눈여겨봐야하는 곳은 다음과 같습니다.

  1. 카메라 방향을 쿼터니언으로 표현한 부분
  2. 키 입력으로부터 3개의 각도를 얻는 부분. 이 3개의 각도는 on/off 스윗치이며, 누적되지 않는 다는 것에 주의하십시오. 저는 while문에 이것들을 리셋했습니다. 물론 이것이 최선의 방법은 아닙니다. 그냥 신속히 처리하기 위해서 그랬을 뿐입니다.
  3. 3개의 각도를 임시 쿼터니언으로 변환하는 부분
  4. 임시 쿼터니언과 카메라 쿼터니언을 해서 합성한 방향을 얻는 부분.  순서에 주의.
  5. 이렇게 해 얻은 카메라 회전을 최종 변환 행렬을 위해 회전축과 각도에 의한 표현으로 변환하는 부분.

키가 눌러졌을 때, 저는 키 입력에 대응하는 고유의 축에서의 작은 회전을 나타내는 임시 쿼터니언을 생성했습니다. 그리고 임시 쿼터니언과 카메라 쿼터니언을 했습니다. 이 4차원 공간에서의 회전을 합성한 것이 짐벌락을 피하게 해줍니다. 여러분이 직접 이것을 해보고 자신의 눈으로 확인해주세요.

카메라 쿼터니언은 최종 변환 행렬로 합성할 수 있도록 행렬이나 그에 상응하는 형태로 변환되야 합니다. 여러분이 쿼터니언을 사용할 때, 3차원 공간과 4차원 공간은 섞일 수가 없기 때문에 항상 이 작업을 해주셔야 합니다. OpenGL의 경우에 저는 그냥 쿼터니언으로부터 회전축을 변경하기만 했고 나머지는 API에게 위임했습니다.

제가 두번째 프로그램에서 오일러 각을 회전에 사용하지 않았지만 저는 여러분이 첫번째 프로그램에서의 오일러 회전에 대해서 볼 수 있게 하기 위해서 오일러 회전 부분을 남겨 두었습니다. 오일러 각은 하나 이상의 축으로 회전시키면 올바르게 되지 않을 것입니다. 왜냐하면 쿼터니언이 카메라 쿼터니언으로부터 오일러 각을 얻는 대신 키 입력을 통해서 계산하기 때문입니다. 두번째 프로그램은 프로그램을 시작했을 때 그냥 여러분이 요 값을 90도로 회전하려는 경우에 짐벌락이 더 이상 일어나지 않는다는 것을 보기 위해서 만든 참고자료일뿐입니다.

Note: 저는 여러분에게 저의 수학 라이브러리를 사용하라고 추천하고 싶지 않습니다. 여러분이 쿼터니언을 이해하고 나서 스스로 짜 보십시오. 참고로 저는 이 코드를 전부 버리고 다시 만들 예정입니다. 이 코드들은 저에게 있어서 너무나 지저분하고 난잡한 코드입니다.

제가 보여드리지 않은 것

여러분이 이미 눈치채셔겠지만 저는 쿼터니언을 오일러 각으로 변환하는 방법에 대해서는 설명하지 않았습니다. 그 이유는 아직까지도 완벽하게 동작하는 변환을 제가 알아내지 못했기 때문입니다. 제가 알고 있는 유일한 방법은 쿼터니언으로부터 행렬을 얻고, 그 행렬로부터 오일러 각을 추출하는 것입니다. 그러나, 오일러에서 행렬 변환은 다대일 관계(sin과 cos으로 인해)이기 때문에 저는 atan2를 사용해서 그 역을 구하는 방법을 알지 못합니다. 만약 정확하게 행렬로부터 오일러 각을 추출하는 방법을 알고 계시다면 저에게 알려주세요.

제가 보여드리지 않은 다른 내용은 행렬을 쿼터니언으로 변환하는 방법입니다. 여러분이 오일러 표현과 회전축과 각도에 의한 표현을 쿼터니언으로 변환할 때 행렬을 거치지 않고도 직접변환이 가능하므로 굳이 행렬을 쿼터니언으로 변환할 필요가 없기 때문입니다.

과제 - SLERP

여러분이 쿼터니언을 완전히 이해했다고 생각하신다면 다시 한번 생각해 보십시오. 아직도 쿼터니언에 대해서 더 배워야 할 게 남아 있습니다. 제가 전에 회전축과 각도(Axis Angle)에 의한 표현이 왜 나쁠까라고 말했던 것을 기억하시나요? '보간'이라는 단어가 갑자기 떠오르지 않나요?

저는 쿼터니언을 사용한 보간에 대해서 설명한 시간을 갖지 못했습니다. 이 글은 제가 예상한 것보다 시간이 오래 걸렸습니다. 여기서 SLERP(구면 선형 보간)에 대한 기본적인 아이디어를 드리겠습니다. 이것은 기본적으로 2개의 쿼터니언 방향 사이에 일련의 쿼터니언들을 생성합니다. 일련의 쿼터니언들은 처음과 마지막 쿼터니언사이에서 부드러운 모션(움직임)의 결과를 제공합니다. 오일러와 회전축 표현으로는 일관성있게 이러한 부드러운 움직임을 만들 수 없습니다.

마지막으로

저는 이 기사가 쿼터니언 이론 뒤에 숨겨진 알수 없는 미스테리들을 속시원하게 없애주었길 바랍니다. 다시한번 마지막으로 여러분에게 당부하고 싶은 말은 서로 다른 좌표계에서 2개의 쿼터니언을 하지 말아주십시오. 이렇게 하시면 여러분은 고통의 시간을 맛보시게 될것입니다.

그러면 새로 찾아낸 쿼터니언의 능력에 여러분이 기뻐하시길 바라면서 저는 이제 그만 여러분과 헤어질까 합니다. 몸조심하시구여. 그리구 다시 만나뵙기를 바라면서...

 

반응형

ttp://cs.sungshin.ac.kr/~hkim/LECTURE/CG/Shading/RayTracing.htm

 

 

 

 

 

Ray Tracing

 

 

 

 


특징


  
  물체를 화면에 표시하는 가장 기본적인 방법은 물체의 3차원 좌표를 시각변환과 원근투영 방법을 이용해서 2차원적 화면 좌표로 변환시키는 것이다. 또한 물체를 화면에 3차원적으로 표현하기 위한 방법 중의 하나인 와이어 프레임에 의한 간단한 표시법으로는 깊이감이 제대로 나타나지 않기 때문에 물체에 실제감을 더해주기 위해 은선과 은면제거 그리고 음영표현 방법을 사용해 보았다.

그러나 위와 같은 방법으로는, 아무리 실감 나는 표현을 한다고 해도 거울이나 투명한 물체 등을 표현하기는 불가능하다. 그것은 지금까지의 표시 방법이 눈과 물체와의 위치관계에 따라 좌표를 변환하는 것을 기본으로 하고 있지만, 거울면에서는 다른 위치에서 바라본 듯한 풍경이 나타나기 때문이다.

ray tracing 은 더욱 현실감 있는 표현이 가능방법으로서 광선추적이라고도 한다. 이것은 반사, 음영 만들기, 즉 유리 물체 등에 있어서의 빛의 굴절을 정확히 모사하고, 보다 현실에 가까운 입체표현이 가능한 방법으로 가장 유효한 랜더링 방법이다 
   Ray Tracing은 광선의 경로를 하나하나 추적하여 눈에 들어오는 빛을 찾아내는 방법인데, 이 방법을 이용하면 굴절과 반사의 효과를 나타내는 데 있어 그리 어렵지 않다. 평범한 다면체들을 그려내기 위해 Ray Tracing 방법으로 광선을 일일이 추적하는 것은 비효율적이지만, 거울이나 투명체를 표현하는 데 있어서는 Ray Tracing이 가장 알맞은 방법이다.

Ray Tracing의 또다른 장점은 은면제거 알고리즘이 따로 필요하지 않다는 것이다. 그 이유는 가려진 물체가 발산하는 빛은 우리의 눈에 직접 들어오지 않고 다른 물체에 닿아버리므로 결국 이 빛은 화면에 표시되지 않기 때문이다. 그러나 광선을 하나씩 추적한다는 말에서 느낄 수 있듯이, 비록 광선추적 알고리즘 자체는 그리 복잡하지 않다 하더라도 전체적인 계산량이 많아진다는 단점이 있다.

 
광선추적법을 이용한 표현


 

 

1. 투명물체와 경면반사의 표현이 가능  
2. 은면소거의 처리가 불필요  
3. 초병렬처리가 가능  

 

 


  
 


광선추적의 기초


 

Ray Tracing이란 관찰자의 눈에 들어오는 광선을 찾아 그 광선의 색을 화면에 표시해주는 방법으로서, 여러 방향에서 관찰자의 눈으로 들어오는 광선들의 색을 정확히 찾기만 한다면 영상을 만드는 일은 아주 간단해진다.

광선을 추적하는 방법에는 두 가지가 있는데, 하나는 광원이 내보내는 빛의 방향을 세분하여 추적하는 정방향 광선추적법이고, 다른 하나는 관찰자의 눈에서 광원방향으로 시선을 추적하는 역방향 광선추적법이다.

(1) 정방향 광선추적법(Forward Ray Tracing)

정방향 광선추적법은 역방향 광선추적법에 비해 자연현상을 좀더 충실하게 시뮬레이션한다는 장점이 있기는 하지만, 광원이 내보내는 여러 방향의 광선을 모두 추적해야 하는 비능률적인 면이 있다. 즉, 우리가 수많은 방향으로 세분화시킨 광선들을 모두 추적한다 해도, 그것들 중 대부분은 우리의 눈에 들어오지 않을 뿐만 아니라 그러한 광선은 영상을 만들어내는 데 전혀 도움이 되지 않기 때문이다. 그러므로 충분한 개수의 광선이 눈(화면)에 들어오도록 만들려면 광원에서 출발한 광선들을 거의 무한하게 세분화시켜야 한다는 것을 알 수 있다. 그러나 광원을 떠나는 빛을 셀 수는 없는 일이므로, 사실 이러한 방법으로 자연현상을 흉내내는 일은 거의 불가능하다.

(2) 역방향 광선추적법(Backward Ray Tracing)

관찰자의 시선방향을 따라가서 빛을 내보낸 물체를 찾아내는 방법이다. 시선이 물체에 닿았다는 것은 그 방향에서 물체의 빛이 눈으로 들어오는 것을 의미하므로, 시선방향으로 빛을 역추적하면 눈에 들어올 광선만을 추적할 수 있게 된다. 그러므로 광원에서 나오는 무한히 많은 빛들 중에서 눈에 들어올 빛을 찾아내는 정방향 광선추적법보다 시선방향으로 빛을 역추적하는 역방향 광선추적법이 훨씬 효율적임을 알 수 있다.

역방향 광선추적법이란 이러한 역추적 방법을 뜻하는 것으로서, 시선의 경로를 따라가다가 빛을 분산시키는 물체에 도달하면 Shading 모델로 광선의 색과 밝기를 결정하는 방법이다. 일반적으로 Ray Tracing이라 하면 광원에서 광선을 추적하는 정방향 광선추적법이 아닌 눈에서 광원으로의 시선추적 방법인 역방향 광선추적법을 의미한다.



Ray Tracing에서는 시선을 추적하므로, 모든 계산이 물체를 나타내는 3차원의 실좌표계(World Coordinate)상에서 이루어진다.

눈에서 출발하는 시선을 설정하는 원리는 눈과 물체사이에 화면을 놓고 화소와 시선을 대응시키는 것이다. 눈에서 출발한 시선을 따라가다가 거울을 만나게 되면 시선을 반사시켜 방향을 바꾸어준 뒤, 시선과 만나는 다른 물체를 찾는 일을 되풀이한다. 그리고 헝겊과 같이 빛을 분산시키는 물체에 시선이 닿게 되면 더 이상 시선을 추적하기 어려우므로 헝겊의 색을 화소에 칠해주는데, 이 경우 단순히 한가지 색채만을 쓰는 것이 아니라 광원과 물체 표면과의 각도 관계를 이용하여 색에 밝기를 더해준다.

Ray Tracing에서는 시선을 직선방정식으로 나타내고, 직선과 물체의 교점을 계산하는 방법을 사용하며, 여러 물체와의 교점 중에서 가장 가까운 교점을 시선이 도달한 곳으로 생각한다.

 

 

 


광선추적 알고리즘


 

매끄러운 평면에 광선이 조사되면, 일부는 반사하고 일부는 내부를 향해 나아간다. 반사광선은 입사각  와 같은 각도로 반사하지만, 내부로 향하는 광선의 방향은 물체를 구성하는 물질의 굴절률에 따라 다르다.  표면에서 분리된 2개의 빛은 다시 다른 표면에 조사되어 분리된다. 아래 그림  (a)와  같이 다수의 반사, 굴절을 반복하여 시점으로 향한 빛이 실제로 관찰  되어진다. 즉, 시점에서 관측되어진 명도 I는 그림(b)의 나무로 표현되는 것과 같이,  반사광, S1과 물체 내부로부터의 투과광T1의 합으로 주어진다. 이 나무구조는  무한히 계속된다. 

종래의 음영모델을 이용하는 경우, 광원으로부터 출발해서 반사, 분리의 반복을 계산하면 시접까지 도달한 빛은 극히 적고, 다른 많은 연산처리가 그래픽 표현에는 불필요하게 되고 만다. 따라서 레이트레이싱에서는 역으로 시점방향에서 출발해서 최종적으로 얻고 싶은 2차원 디스플레이상의 화살의 각 화소점만의 컬러정보를 얻는 수법을 이용하고 있다.  
그것은 옆 그림에서 나타나듯이, 시점 I로부터 화상평면의 각 화소점 (m,n)을 향해서 단위광원을 방사하여 그 광선과 물체의 교점을 구하는 작업과 교점에 잇어서 물체의 컬러정보를 구하는 작어븡로 구성된다. 그때, 각 화소점과 핀홀을 연결한 광선이 대상물체와 한번도 교차하지 않으면 배경색(예를 들어 흑)이 된다. 혹시 물체와 교차하면 대상물체가 불투명한 경우는 그림자 모델에 의해 그 점의 밝기를 계산한다. 

이상을 정리하면 다음과 같은 알고리즘이 되고 모든 것이 투명 물체인 경우의 기본적인 레이트레이싱법이 된다.  
 

1. m,n의 초기값을 설정한다.  
2. 시점I로부터 화소(m,n)에 광선을 방사하고, 모든 물체와의 교점을  구한다.  
3. 교점이 존재하면 화상평면에 가장 가까운 교점을 구한다.  
   교점이 존재하지 않으면 5번으로 간다.  
4. 3점의 물체의 그림자 만들기를 행하고, 색을 결정해서 화소(m,n) 에 부여한다.  
5. 화소(m,n)를 배경색으로 한다.  
6. 이상의 처리를 모든 화소에 대해 행한다. 

 

유리구 등과 같이 투명한 물체의 경우는 스넬의 법칙에 나타내듯이, 반사광 그 속으로 들어가는 투과광을 생각해서 임사되어진 단위광량 1을 적당한 비율로 반사광과 투과광에 분배한다. 그리고, 각각의 빛이 투명한 물체에 닿은 경우, 각각이 가진 광량에 비례해서 각 불투명 물체의 색을 혼합하고, 화소점의 컬러 정보로 한다. 혹시 두 개로 나뉘어진 빛이 다시 투명물체에 닿았을 때는 똑 같이 반복반사와 투과를 계산한다. 이렇게 해서 가지고 있는 광량이 어떤 명도 이하가 되든가, 반사의 횟수가 어떤 제한을 넘으면 계산을 그만두게 하면 된다. 이와 같은 계산을 화상평면상의 모든 화소점에 대해서 행하면 레이트레이싱 화상이 구해진다. 아래의 왼쪽그림은 렌즈 등의 투명한 유리구가 있는 경우의 레이트레이싱 화상 예이고, 아래의 오른쪽 그림은 경면반사가 있는 경우 화상의 예이다. 

 

심화 학습

Recursive Ray Tracing

 

  
 
 


광선 교점의 계산


  
레이트레이싱에서는 교점 계산에 많은 시간이 소요된다. 따라서, 물체와의 교점 계산법에 대해서 살펴보자.
 

시점의 위치를 P0(x0,y0,z0)으로 하고, 디스플레이상의 점을 Pmn(x1, y1, z1) 으로 하면, P0에서 Pmn에 도달하는 광선은 t를 이용하면 아래의 식으로 주어진다. 
 
여기서 
 
라고 놓으면 

 

가 된다. 이때 t를 음의 값으로 하면 광선은 시점으로부터 멀어지는 방향이 되고, t를 1보다 크게 하면, Pmn으로부터 물체가 존재하는 공간으로 들어오는 것이 된다. 이렇게 해서 모든 물체와의 교차판정을 행하고 가장 시점에 가까운 교점과 그 물체를 구한다.  
 
광선과 구의 교점

대상물체가 다음 식으로 나타내어지는 구체라고 하면 

 
식 2을 식 3에 대입하면 

가 된다. 이식은 t의 2차방정식이다. 따라서 이 식이 실근을 가지지 않으면 구와 광선과의 교점은 없는 것이 되고, 하나의 실근을 가지면 접하고, 두 개의 실근을 가질 때에는 작은 쪽의 t가 최초의 교점의 위 값이 된다.  
  

 
광선과 면의 교점

대상 물체가 폴리곤으로 구성되어 있는 경우는 우선 처음에 광선과 폴리곤을 포함하는 평면과의 교점을 구하고, 그 교점이 폴리곤의 내부에 존재하는지의 여부를 판정하게 된다.  
폴리곤을 포함하는 평면의 방정식을 다음 식으로 한다. 

여기에 식2를 대입한다. 

 

이 식으로부터 다음이 구해진다.  
  

혹시 t = 0 이 되면 평면과 광선은 평행이 되어 교차하지 않는 것이 된다. 교점이 폴리곤 내부에 있는지 여부의 가장 간단한 판정방법은 아래 그림에 나타나듯이 , 폴리곤과 교점 P를 xz평면상에 투영하는 것이다. 투영한 점 P 가 투영되어진 폴리곤 내부에 존재하면 교점이 존재하는 것이 된다.  
  

 


고속화 기술


 

레이트레이싱은 효과적인 렌더링법이지만 디스플레이 평면의 해상도가 높을수록 광선의 수가 증가하고 처리시간이 걸린다. 또 대상물체가 증가할수록 광선과 물체와의 교차를 판정하는 시간이 걸리고, 처리 시간이 방대해지는 결점이 있다.  그 때문에 다수의 고속화 알고리즘이 개발되어 있다.  고속화의 방식에는 하드웨어 방식과 소프트웨어 방식이 있다.  
하드웨어 방식에 대해서는, 레이트레이싱에서는 각 화소점의 계산이 독립적이어서 다른 영향을 받지 않는다는 사실로부터 병렬처리가 가능하고,기본적으로 프로세서의 수를 증가시키면 그 만큼 고속화가 실현된다.  그 때문에 많은 병렬처리 시스템이 제안되어 있다. 

(1) Supersampling

Ray Tracing에 의한 영상을 얻기 위한 기본적인 표시법은 화소 하나에 시선 하나씩을 대응시키는 방법이다. 이 방법의 결점은 물체 사이의 경계부분에서 색의 차이가 심하면 경계의 형태가 마치 계단과 같은 모습으로 나타난다는 것이다.

Supersampling은 이러한 한계를 개선하기 위해 고안된 방법으로서, 화소를 더 세분하고 하나의 화소에 여러개의 시선을 설정하여 추적하는 방법이다. Supersampling에서는 세분한 시선들의 밝기를 평균하여 화소의 색을 얻는데, 이는 높은 해상도의 가상적인 영상을 화면의 해상도로 낮추어 표시하는 것과 같다. 물론 이 방법으로 모든 결점을 완전히 해결할 수 있는 것은 아니지만, 색의 변화가 심한 부분에 중간색이 나타나므로 보다 현실감이 높은 영상이 얻어진다.

Supersampling을 한다고 해도 광선의 방향을 일정한 간격으로 세분하는 방법을 사용하면 경우에 따라서 아주 이상한 영상을 만들어내는 수가 있는데, 일 예로 광선의 세분간격과 비슷한 간격의 무늬를 가지는 물체는 그 무늬가 제대로 표현되지 않는다. 광선을 아무리 세분한다고 해도 일정한 간격으로 세분한 이상은, 이러한 현상을 완전히 막을 수는 없다.

그러나 현실감을 높이는 대신 그만큼 계산량이 늘어나게 되는데, 예를 들어 화소를 2×2의 격자로 세분한다고 하면 대략 계산량이 4배로 늘어나게 된다.

(2) Distributed Ray tracing

이러한 현상을 막기 위해 고안된 것이 distributed ray tracing(광선의 분산 추적)이다. 이것은 시선의 방향에 난수를 더하여 "확률적 분포"를 가해주는 방법으로서, 시선을 설정할 때나 반사와 굴절을 계산할 때 방향을 불규칙하게 흐트러뜨리면 일정한 시선간격에 의한 결점이 나타나지 않을 뿐만 아니라 빛이 번지는 효과도 얻을 수 있다는 원리를 이용한 것이다. 그러므로 그림자의 계산에 이 방법을 도입하면 그림자의 경계면이 흐려지는 효과를 얻을 수 있다.

Distributed Ray tracing 방법을 사용하였을 경우, 영상 전체를 보아서는 빛이 번지는 효과가 난다고는 할 수 있겠지만, 실제에 있어서는 난수에 의해 영상의 세부에 거칠 거칠한 무늬가 나타나게 된다. 그러므로 부드럽게 빛이 번지는 효과를 얻으려면 Supersampling 방법과 병행하여 사용해야 한다.

(3) 공간분할법(Space Subdivision)

Ray Tracing의 계산시간이 많이 걸린다는 단점을 보완하기 위한 방법으로 공간분할법은 물체를 정의한 실 좌표계 공간을 여러 영역으로 세분하고 물체를 영역별로 분류하여, 시선이 지나가는 영역내에 있는 물체들로만 교점테스트를 하므로 불필요한 계산을 줄일 수 있다.

즉, 각 영역에 물체가 하나씩만 있도록 세분한 다음 시선이 지나가는 영역을 효율적으로 찾기만 한다면 교점계산의 부담이 많이 줄어든다.

왼쪽그림과 같이 대상물체가 존재하는 공간을 8분할하고, 그 부분영역을 다시 8분할해 나가서 적당한 크기의 입방체가 될 때까지 부분분할해 간다. 이때, 각 부분영역과 대상물체와 관계는 아래 그림에 나타나듯이 무관계, 일부분관통,내부에 존재의 3가지가 있다. 그리고 광선이 직진하면 순차통과하는 부분영역이 구해지고, 각 부분영역마다 대상물체와의 관계가 구해져 있기 때문에, 그림 b,c,의 경우에 대해서만 교점계산을 행하면 된다. 처음에 대략적인 부분영역을 이용해서 판정하고, 혹시 b,c의 경우가 되면 재차 8분할하는 것보다 효율적으로 교점이 구하여 지게 된다. 부분영역의 계산기 내부에서의 기억방식으로 8분할 트리표현(octree structure)이 이용되어지고 있다. 

(4) 은면제거 알고리즘의 이용

반사체가 많은 그림이라도 시선의 방향이 변하는 평균횟수는 얼마되지 않는다. 또한 반사체나 투명체가 전체 그림에서 차지하는 면적도 얼마 되지 않는다. 그러므로 반사체나 투명체를 제외한 부분을 기존의 은면제거 알고리즘으로 그려낸 후 남은 부분에 대해서만 Ray Tracing을 행하면 계산량을 상당히 줄일 수 있을 것이다.


(5) 볼록포의 이용

대상물체가 복잡한 형상을 하고 있으면, 광선과 대상물체의 교차 여부의 판정에 시간이 걸리며 전체처리 시간의 95%이상이 소요된다고 말하여 진다. 따라서 대상이 되는 입체를 내부에 포함하는 간단한 볼록입체를 이용하고, 이 입체를 새로이 볼록포라고 부르기도 한다. 볼록포와 광선의 교차를 우선 구하여 교차하지 않으면, 원래의 입체는 교차하지 않음을 확실히 알 수 있다.혹시 볼록포와 광선이 교차하고 있으면 실제로 대상물과 광선이 교차하는지의 여부를 종래대로 광선추적의 처리로 구한다.  
볼록포는 교차계산이 용이한 입방체와 구가 이용되어진다. 그러나, 최적형상의 볼록포의 결정은 어려우므로, 현재에도 여러 가지 검토가 행하여 지고 있다. 보통은 아래 그림에 나타나듯이, 볼록포를 광선에 수직한 면에 투영했을 때,볼록포내의 물체가 포함되지 않은 영영(void area)이 적은 쪽이 바람직하다. 

반응형

'수학 (Mathematics) > 3D수학' 카테고리의 다른 글

짐벌락 피하는 방법  (0) 2012.11.02
쿼터니언  (0) 2012.11.02
크래머 공식  (0) 2012.11.02
행렬식  (0) 2012.11.02
1차 독립  (0) 2012.11.02

[여인수로 행렬식 계산하기]

 

A= ( 3 X 3 )  행렬이 있다고 가정

 

행 기준

3

∑ A_kj = (-1)^(k+j) a_kj * M_kj

j=1

 

열 기준

3

∑ A_jk = (-1)^(j+k) a_jk * M_jk

j=1

 

 

k    = 사용자 지정 행 번호  , if k=1 일때 1 행에 대한 연산 수행

a_kj = A 행렬에 해당하는 k,j 번째 원소

M_ij = 해당 k 행  ,j 열 을 제외하면 (2X2) 행렬이 나오고 이것에 대한 행렬식을 계산한다

 

총 3회의 연산으로 행렬식 연산을 마친다

 

 

 

 

 

 




http://carstart.tistory.com/160


[선형대수학] 5. 크래머 공식 (Cramer's Rule)


2010. 12. 27 (月)





연립일차방정식은 중학교 인지 고등학교인지는 모르겠지만 아무튼 수학시간에 배웠다.

이런 연립일차방정식을 중고등학교때는 간단하게 손으로 풀수 있는 수식이다.

하지만 대학 이상 수준에서는 간단하게 손으로 풀수 있는 정도 일가? 물론 아니다 ^^





이 연립방정식을 우리가 중고등학교 때 필수로 배우라 한 이유가 무엇일가 ?

실질적으로 우리 생활 회로해석이라든지 3D, 영상처리, 천문분야 등등 다양한 분야에서는 
이 연립 방정식을 계산할 때 많이 사용한다. 물론 간단한 연립방정식이 아니다. 
그래서 이를 미리 연습해본 것이다.
이제는 대학와서 복잡한 연립방정식을 풀게 된다. 
우리는 지금까지 이를 풀기위해 행렬을 배웠고 행렬식을 푸는 방법에 대해서 배웠다. 






그럼 이런 복잡한 연립방정식을 어떻게 풀죠?

보통 연립방정식 또는 행렬식을 푸는데는 

대치법 (substitution)
가우스 소거법 (Gaussian elimination)
크레머 공식(Cramer's Rule))

다양한 방법이 있다.





그럼 크래머 공식은 무엇인가요 ?

다음과 같은 n까지 있는 연립방정식을 풀려고 한다.



우리는 이를 풀기 쉽게 계산하기 위해 행렬로 표현할 수가 있다.

    B       =                 A                       X


여기에서 위의 행렬을 다음과 간단하게 나타낼 수 있다.
B = AX

여기서 


A 는 n * n 정방행렬 
X 와 B 는 n * 1 인 열벡터 임을 알 수있다.



내용 추가 :
A : 계수행렬
A*x = b   인데

이때 |A| != 0  A의 행렬식이 0 이 아니면 가역(역행렬을 갖으면) 다음과 같은 연산이 가능하다
A(A^-1)*x = (A^-1)b
I*x = (A^-1)b
x = (A^-1)b

그런데 A^-1 = (  1/|A| adj_A )  임으로
x = (  1/|A| adj_A )b

이를 정리하면 x에 대해 구할 수 있다, 자세한 증명은 패스

간단요령은 x 를 행렬 A 의 각 열에 넣는데 이때 
각 열에 넣을때 b 가 들어가는 열은 adj_A 연산에서 b 부분은 i,j 행열을 제외한 행렬식 연산후 b 의 값을 곱해주는
용도로만 사용되기 때문에 b 가 A 행렬 안에 들어갈 수 있는 형태가 된다





각각의 해는 다음과 같이 표현이 가능합니다. 


     는 행렬 A의 행렬식값 나타냅니다.
  n 는 행렬 A에서 제n열의 값을 B로 바꾼 행렬식값을 나타냅니다.

이를 크래머 공식이라고 합니다.

여기서 수학적인 증명은 생략하도록 하겠습니다. ^^;;





크래머 공식을 하기위한 조건이 있나요 ?

B = AX

여기서 B 가 영행렬 이면 이를 동차라 하고 그렇치 않으면 비동차라 합니다.
그리고 A 가 정칙행렬(역행렬이존재) 하는 경우에는 비동차연립일차방정식으로 AX =B는 유일한 해를 가지게 됩니다.
이 조건을 만족하면 크래머 공식이 성립합니다.





말로는 설명이 어려워요 한가지 예를 들어 설명해 주세요!


간단하게 3개의 연립방정식을 풀어보겠습니다.



이를 행렬식으로 표현합니다.
   B         =                A                       X

   
B 행렬은 0 행렬이 아닙니다.
A 행렬은 역행식 값이 0이 아니기 때문에 정칙행렬 입니다. 


두 조건을 만족하니 크래머 공식을 이용하여 각각의 해를 구합니다.

 
 



결과 입니다 ^^

반응형

'수학 (Mathematics) > 3D수학' 카테고리의 다른 글

쿼터니언  (0) 2012.11.02
광선추적 (Ray Tracing)  (0) 2012.11.02
행렬식  (0) 2012.11.02
1차 독립  (0) 2012.11.02
3차 곡선(Cubic polynomial curves )  (0) 2012.11.02



http://blog.naver.com/rlawldyd0806/50036277924




행렬식



-행과 열이 같은 정방 행렬에서만 구할 수 있다

정방 행렬 A에 대한 행렬식 표현

detA = det(A) = |A|

 

행렬식을 계산하기 위해 필요한 두 가지 개념

1. 소행렬식(minor)이라는 것으로, (i, j)소행렬식 Mij 는 nXn정방 행렬A에서i번째행과

    j번째 열을 제외한 (n-1)X(n-1) 정방 행렬의 행렬식을 의미

2. 여인자(cofactor)라는 것으로 ij = (-1)i+jij로 표현

 

소행렬식 및 여인자 예)

 가 주어졌을 때, 여인자 12 와 31은?

여인자 식에 대입하면 12 = (-1)1+231와 31 = (-1)3+131된다.

 

소행렬식  12  

소행렬식 31  

 여인자  여인자

 

D3DX함수

여인자를 구하는 함수는 지원하지 않는것 같다.

 

사용자함수

//소행렬식을 구하여 여인자 구한다.

D3DXMATRIX *MYMatrixCofactor(D3DXMATRIX *pOut, D3DXMATRIX *pMat)
{
 pOut->_11 = (pMat->_22 * pMat->_33 - pMat->_23 * pMat->_32);
 pOut->_12 = -(pMat->_21 * pMat->_33 - pMat->_23 * pMat->_31);
 pOut->_13 = (pMat->_21 * pMat->_32 - pMat->_22 * pMat->_31);

 pOut->_21 = -(pMat->_12 * pMat->_33 - pMat->_13 * pMat->_32);
 pOut->_22 = (pMat->_11 * pMat->_33 - pMat->_13 * pMat->_31);
 pOut->_23 = -(pMat->_11 * pMat->_32 - pMat->_12 * pMat->_31);

 pOut->_31 = (pMat->_12 * pMat->_23 - pMat->_13 * pMat->_22);
 pOut->_32 = -(pMat->_11 * pMat->_23 - pMat->_13 * pMat->_21);
 pOut->_33 = (pMat->_11 * pMat->_22 - pMat->_12 * pMat->_21);

 return pOut;
}

 

여인자를 이용한 행렬식 표현

동일한 열 성분(a1j, a2j•••, anj)에 대해서 표현

 

 

동일한 행 성분(aj1, aj2•••, ajn)에 대해서 표현

 

 

1X1정방 행렬  A=[a]의 행렬식 |A|=a

 

2X2정방 행렬 의 행렬식 계산

 

2X2정방 행렬식은 대각선 성분의 곱(a11a22)을 비대각선 성분의 곱(a12a21)으로 뺀 모양이다

 

3X3정방 행렬의 행렬식

       

    

 

 Direct3D 함수

FLOAT D3DXMatrixDeterminant(CONST D3DXMATRIX *pM);

 

사용자 함수

//행렬의 행렬식을 구한다.

float MYMatrixDeterminant(D3DXMATRIX *pM)
{
 return (pM->_11*pM->_22*pM->_33) + (pM->_12*pM->_23*pM->_31) + (pM->_13*pM->_21*pM->_32)
      - (pM->_11*pM->_23*pM->_32) - (pM->_12*pM->_21*pM->_33) - (pM->_13*pM->_22*pM->_31);
}

반응형

'수학 (Mathematics) > 3D수학' 카테고리의 다른 글

광선추적 (Ray Tracing)  (0) 2012.11.02
크래머 공식  (0) 2012.11.02
1차 독립  (0) 2012.11.02
3차 곡선(Cubic polynomial curves )  (0) 2012.11.02
곡선, SVG 곡선 사이트 링크들  (0) 2012.11.02

V1 + V2  을

상수 C1, C2 를 각각에 곱해 + 로 연결한 것을

C1V1 + C2V2 => 선형 결합 이라고 하며

이때 


C1V1 + C2V2 = 0 이 되는 것이

C1,C2 가

C1=C2=0 일때만 C1V1 + C2V2 = 0 이 성립되는 것을

1차 독립이라고 한다


x + 2x = 0 은 1차 독립이 아니다

ax + 2x => a =-2 면 0 이 됨으로

e^x * e^2x 은

a1 e^x * a2 e^2x => a1=a2=0 일 때만 결과가 0 이 되면 1차 독립

이게 a1=a2=0 가 0 일때만 결과가 0 이 되는지는 검증을 해봐야 되는데 아무튼... 

상수가 0 인가 그리고 그때뿐인가가 중요한 포인트

반응형

+ Recent posts