'수학 (Mathematics) > 3D수학' 카테고리의 다른 글
파이프라인 연산 (0) | 2023.03.11 |
---|---|
입체각(스테라디안) (0) | 2013.07.25 |
투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(norm) 을 구하는 방법과 d3dx9math.h (0) | 2013.07.09 |
점,벡터 구분의 미묘함 (0) | 2013.05.08 |
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
파이프라인 연산 (0) | 2023.03.11 |
---|---|
입체각(스테라디안) (0) | 2013.07.25 |
투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(norm) 을 구하는 방법과 d3dx9math.h (0) | 2013.07.09 |
점,벡터 구분의 미묘함 (0) | 2013.05.08 |
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
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]
공간에서 O를 한 끝점으로 하는 사선(射線:ray) OA가 O의 둘레를 회전하여 처음의 위치로 되돌아올 때, 그려진 도형을 입체각이라 하며 O를 꼭짓점이라 한다. 이 경우 입체각의 크기는 O를 중심으로 하여 반지름 1인 구(球)가 이 입체각의 변과 만나서 이루어지며, 구면 위의 도형 S의 넓이로 측정된다.
S의 넓이가 1일 때, 이 입체각을 1sr(스테라디안:steradian)이라고 한다. 이것이 입체각의 단위이며, 평면의 경우의rad(라디안:radian)의 정의를 구면 위로 확장한 것이라고 할 수 있다. 또 전 구면의 중심점에 대한 입체각의 1/(4π)이 1sr이 된다.
[네이버 지식백과] 입체각 [solid angle, 立體角] (두산백과)
스테라디안(steradian, 기호 sr)은 입체각의 국제 단위이다. 평면에서 각도를 나타내는 라디안처럼, 3차원 공간에서 각도로 나타나는 2차원의 영역을 나타낼때 사용된다. 용어는 견고함을 의미하는 그리스어: στερεός
스테레오스[*]와 광선을 의미하는 라틴어: radius
라디우스에서 유래한다.
스테라디안은 라디안 처럼 무차원상수이다. 즉, 1 sr = m2·m-2 = 1. 구 전체의 입체각은 4 sr이 된다.
1 스테라디안은 반지름이 r인 구의 표면에서 r2인 면적에 해당하는 입체각이다.
면적 A가 r2 와 같고, 반구의 표면적 (
)과도 같다면,
이 관계를 따르게 된다. 그러므로, 각
를 갖는 콘의 입체각은 다음과 같아진다.
이것은 축으로부터의 각(apex angle)의 2
≈ 1.144 rad or 65.54°에 대응된다.
구의 면적이
r2이므로, 정의에 의해 구는 4
=12.56637 스테라디안이다. 같는 논리로, 최대 입체각은 4
sr 이다. 스테라디안은 squared radian으로 불리기도한다.
스테라디안은 1 라디안의 angle excess(angle excess)를 갖는 다각형의 구면, 또는 완전한 구의 1/(4
), 또는 (180/
)2 제곱 각(square degree), 또는 3282.80635 제곱 각과 같다.
스테라디안은 SI 보조 단위였으나, 1995년 SI 보조 단위가 폐지 되면서, SI 유도 단위가 되었다.
2차원 평면에서, 라디안으로 표현되는 각은 잘려지는 호의 길이와 관련이 있다.
3차원에서, 스테라디안으로 표현되는 입체각은 잘려지는 면적과 관련이 있다.
호도법의 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
파이프라인 연산 (0) | 2023.03.11 |
---|---|
구현한 범프 매핑 설명(탄젠트 공간) (0) | 2014.12.09 |
투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(norm) 을 구하는 방법과 d3dx9math.h (0) | 2013.07.09 |
점,벡터 구분의 미묘함 (0) | 2013.05.08 |
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(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
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).
Out = V1 + V2
Out = V1 + f⋅(V2−V1) + g⋅(V3−V1)
/ | 0 | 1 | 0 | 0 | \ | / | V1 | \ | |
| | | | | | | | ||||||
| | −0,5 | 0 | 0,5 | 0 | | | | | V2 | | | |
Out = (1,s,s²,s³) | | | | | | | | | |||||
| | 1 | −2,5 | 2 | −0,5 | | | | | V3 | | | |
| | | | | | | | ||||||
\ | −0,5 | 1,5 | −1,5 | 0,5 | / | \ | V4 | / |
Result = U ⋅ V = ∑iUiVi
/ | 1 | 0 | 0 | 0 | \ | / | V1 | \ | |
| | | | | | | | ||||||
| | 0 | 0 | 1 | 0 | | | | | V2 | | | |
Out = (1,s,s²s³) | | | | | | | | | |||||
| | −3 | 3 | −2 | −1 | | | | | T1 | | | |
| | | | | | | | ||||||
\ | 2 | −2 | 1 | 1 | / | \ | T2 | / |
Result = |V| = √(∑iVi²)
Result = |V|² = ∑iVi²
Out = V1 + s(V2−V1);
Outi = max(Ui, Vi)
Outi = min(Ui, Vi)
Out = 1⁄|V|⋅V
Out = sV
Out = V1 − V2
Result = UxVy − UyVx
a = (Vx, Vy, 0, 1)
b = (a×M)T
Out = (bx, by)
a = (Vx, Vy, 0, 1)
b = (a×M)T
Out = 1⁄bw(bx, by)
a = (Vx, Vy, 0, 0)
b = (a×M)T
Out = (bx, by)
Out = V1 × V2
a = (Vx, Vy, Vz, 1)
b = a×World×View×Proj
c = b⁄bw
Outx = ViewportX + ViewportWidth*(1+cx)/2
Outy = ViewportY + ViewportHeight*(1-cy)/2
Outz = ViewportMinZ + cz*(ViewportMaxZ-ViewportMinZ)
a = (Vx, Vy, Vz, 1)
b = (a×M)T
Out = (bx, by, bz)
a = (Vx, Vy, Vz, 1)
b = (a×M)T
Out = 1⁄bw(bx, by, bz)
a = (Vx, Vy, Vz, 0)
b = (a×M)T
Out = (bx, by, bz)
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 = 1⁄bw(bx, by, bz)
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
Out = (V×M)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 | / |
Result = det M
/ | 1 | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 1 | 0 | 0 | | | |
Out = E = | | | | | ||||
| | 0 | 0 | 1 | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
D = det M
Out = M−1
Result = M≡E
v = Normalized(Eye−At)
l = Up×v
u = v×l
/ | lx | ux | vx | 0 | \ | |
| | | | |||||
| | ly | uy | vy | 0 | | | |
Out = | | | | | ||||
| | lz | uz | vz | 0 | | | |
| | | | |||||
\ | −l⋅Eye | −u⋅Eye | −v⋅Eye | 1 | / |
v = Normalized(At−Eye)
r = Up×v
u = v×r
/ | rx | ux | vx | 0 | \ | |
| | | | |||||
| | ry | uy | vy | 0 | | | |
Out = | | | | | ||||
| | rz | uz | vz | 0 | | | |
| | | | |||||
\ | −r⋅Eye | −u⋅Eye | −v⋅Eye | 1 | / |
Out = M1×M2
Q = (f−n)−1
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | −Q | 0 | | | |
| | | | |||||
\ | 0 | 0 | −Qn | 1 | / |
Q = (f−n)−1
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | Q | 0 | | | |
| | | | |||||
\ | 0 | 0 | −Qn | 1 | / |
Q = (f−n)−1
w = r−l
h = b−t
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | −Q | 0 | | | |
| | | | |||||
\ | −(r+l)⁄2 | −(t+b)⁄2 | −Qn | 1 | / |
Q = (f−n)−1
w = r−l
h = b−t
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | Q | 0 | | | |
| | | | |||||
\ | −(r+l)⁄2 | −(t+b)⁄2 | −Qn | 1 | / |
Q = (f−n)−1
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | −Qf | −1 | | | |
| | | | |||||
\ | 0 | 0 | −Qnf | 0 | / |
Q = (f−n)−1
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | Qf | 1 | | | |
| | | | |||||
\ | 0 | 0 | −Qnf | 0 | / |
Q = (f−n)−1
y = cotg(Fovy)⁄2
/ | y⁄Aspect | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | y | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | −Qf | −1 | | | |
| | | | |||||
\ | 0 | 0 | −Qnf | 0 | / |
Q = (f−n)−1
y = cotg(Fovy)⁄2
/ | y⁄Aspect | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | y | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | Qf | 1 | | | |
| | | | |||||
\ | 0 | 0 | −Qnf | 0 | / |
Q = (f−n)−1
w = r−l
h = b−t
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | (r+l)⁄w | (t+b)⁄h | −Qf | −1 | | | |
| | | | |||||
\ | 0 | 0 | −Qnf | 0 | / |
Q = (f−n)−1
w = r−l
h = b−t
/ | 2⁄w | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 2⁄h | 0 | 0 | | | |
Out = | | | | | ||||
| | −(r+l)⁄w | −(t+b)⁄h | Qf | 1 | | | |
| | | | |||||
\ | 0 | 0 | −Qnf | 0 | / |
(a,b,c,d) = (Planea,Planeb,Planec,Planed)⁄√(Planea²+Planeb²+Planec²)
/ | 1−2a² | −2ba | −2ca | 0 | \ | |
| | | | |||||
| | −2ab | 1−2b² | −2cb | 0 | | | |
Out = | | | | | ||||
| | −2ac | −2bc | 1−2c² | 0 | | | |
| | | | |||||
\ | −2ad | −2bd | −2cd | 1 | / |
s = sin Angle
c = cos Angle
d = 1−c
(x,y,z) = V
/ | dx²+c | dxy+zs | dxz−ys | 0 | \ | |
| | | | |||||
| | dxy−zs | dy²+c | dyz+xs | 0 | | | |
Out = | | | | | ||||
| | dxy+ys | dyz−xs | dz²+c | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
(x,y,z,w) = Q
/ | 1−2y²−2z² | 2xy+2zw | 2xz−2yw | 0 | \ | |
| | | | |||||
| | 2xy−2zw | 1−2x²−2z² | 2yz+2xw | 0 | | | |
Out = | | | | | ||||
| | 2xz+2yw | 2yz−2xw | 1−2x²−2y² | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
s = sin Angle
c = cos Angle
/ | 1 | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | c | s | 0 | | | |
Out = | | | | | ||||
| | 0 | −s | c | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
s = sin Angle
c = cos Angle
/ | c | 0 | −s | 0 | \ | |
| | | | |||||
| | 0 | 1 | 0 | 0 | | | |
Out = | | | | | ||||
| | s | 0 | c | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
(sa,sb,sc) = sin (Roll, Pitch, Yaw)
(ca,cb,cc) = cos (Roll, Pitch, Yaw)
/ | ca⋅cc+sa⋅sb⋅sc | −sa⋅cc+ca⋅sb⋅sc | cb⋅sc | 0 | \ | |
| | | | |||||
| | sa⋅cb | ca⋅cb | −sb | 0 | | | |
Out = | | | | | ||||
| | −ca⋅sc+sa⋅sb⋅cc | sa⋅sc+ca⋅sb⋅cc | cb⋅cc | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
s = sin Angle
c = cos Angle
/ | c | s | 0 | 0 | \ | |
| | | | |||||
| | −s | c | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | 1 | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
/ | x | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | y | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | z | 0 | | | |
| | | | |||||
\ | 0 | 0 | 0 | 1 | / |
(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 | \ | |
| | | | |||||
| | −xb | f−yb | −zb | −wb | | | |
Out = | | | | | ||||
| | −xc | −yc | f−zc | −wc | | | |
| | | | |||||
\ | −xd | −yd | −zd | f−wd | / |
D3DXMatrixTranslation(A, -Scenterx, -Scentery, -Scenterz)
D3DXMatrixScaling(B, Scalingx, Scalingy, Scalingz)
D3DXMatrixRotationQuaternion(C, Srot)
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
/ | 1 | 0 | 0 | 0 | \ | |
| | | | |||||
| | 0 | 1 | 0 | 0 | | | |
Out = | | | | | ||||
| | 0 | 0 | 1 | 0 | | | |
| | | | |||||
\ | x | y | z | 1 | / |
Out = MT
Result = (Pa, Pb, Pc, Pd)⋅V
Result = (Pa, Pb, Pc)⋅V + Pd
Result = (Pa, Pb, Pc)⋅V
n = (Planea, Planeb, Planec)
d = V − U
Out = U − d⋅(Pd + n⋅U)⁄(d⋅n) [iff d⋅n ≠ 0]
Planea = Nx
Planeb = Ny
Planec = Nz
Planed = −N⋅P
q = 1⁄√(Pa² + Pb² + Pc²)
Outa = q⋅Pa
Outb = q⋅Pb
Outc = q⋅Pc
Outd = q⋅Pd
v = (B − A) × (C − A)
n = 1⁄|v| v
Outa = nx
Outb = ny
Outc = nz
Outd = −n⋅A
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 = −qv⋅B
구현한 범프 매핑 설명(탄젠트 공간) (0) | 2014.12.09 |
---|---|
입체각(스테라디안) (0) | 2013.07.25 |
점,벡터 구분의 미묘함 (0) | 2013.05.08 |
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
Catmull-Rom 스플라인 곡선 D3DXVec3CatmullRom (0) | 2013.03.21 |
점,벡터 구분의 미묘함을 간략하게 정리한다면
벡터 : 특정 점에서 특정 점까지 상대적 차이 값, 이를 때에따라 단위벡터로 나타낼 수도 있다
점 : 원점에서 시작해 어떤 특정 점까지의 위치
p.s ;;;; 너무 간단한가, 복잡하게 말할것도 없음으로.. ㅎ
입체각(스테라디안) (0) | 2013.07.25 |
---|---|
투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(norm) 을 구하는 방법과 d3dx9math.h (0) | 2013.07.09 |
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
Catmull-Rom 스플라인 곡선 D3DXVec3CatmullRom (0) | 2013.03.21 |
Fast Overlap Test for OBBs (0) | 2013.03.20 |
원문 : http://www.gamasutra.com/features/19980703/quaternions_01.htm
작년은 하드웨어 가속의 시대로서의 역사에 남겨졌다고 할 수 있다. 폴리곤을 래스터라이즈화하고 텍스처 매핑을 하는 많은 작업들이 전용 하드웨어에 넘겨졌다. 결과적으로 우리 게임 개발자들은 이제 물리적 시뮬레이션 및 다른 기능들을 위해 많은 CPU 사이클을 절약할 수 있게 되었다. 쿼터니온 덕택에 그러한 부가적인 사이클들이 회전 및 애니메이션을 부드럽게 하는 것과 같은 작업을 위해 적용될 수 있게 되었다.
많은 게임 프로그래머들은 이미 쿼터니온의 대단한 세계에 대해서 발견했으며, 그것들을 광범위하게 사용하기 시작했다. TOMB RADIDER 타이틀을 포함한 일부 삼인칭 게임들은 그것의 카메라 움직임을 애니메이션하기 위해서 쿼터니온 회전을 사용한다. 모든 삼인칭 게임은 플레이어의 캐릭터의 측면이나 뒤에서 따라가는 가상 카메라를 가지고 있다. 이 카메라는 캐릭터와는 다른 동작을 통해(즉 다른 길이의 호(arc)를 통해서) 움직이기 때문에, 카메라 동작은 (플레이어의) 행동을 따라가기에는 부자연스럽고 플레이어에 비해 너무 "변덕"스러웠다. 이것이 쿼터니온이 해결사로 나서게 된 하나의 영역이다.
오브젝트의 방향을 표현하기 위한 많은 방식이 존재한다. 대부분의 프로그래머들은 3x3 회전 행렬이나 세 개의 오일러(Euler) 각을 사용해 이 정보를 저장한다. 이러한 해결책들은 오브젝트의 두 방향 사이를 부드럽게 보간하려고 하기 전에는 잘 동작한다. 사용자가 제어하지 않고 단순히 공간을 자유롭게 회전하는 오브젝트를 상상해 보자(예를 들어 회전문). 만약 당신이 문의 방향을 회전 행렬이나 오일러 각으로 저장하기로 한다면, 당신은 회전 행렬의 값들을 부드럽게 보간하는 것은 계산적으로 비용이 많이 들고 쿼터니온 보간보다는 플레이어의 시점에 대해서 정확하고 부드럽게 보이지 않는다는 것을 알게 될 것이다.
이 문제를 행렬이나 오일러 각을 사용해 해결하고자 한다면, 애니메이터는 단순히 기정의된(keyed) 방향의 숫자를 증가시키기만 해야 할 것이다. 그러나 아무도 얼마나 많은 방향이 충분한 것인지 알 수는 없다. 왜냐하면 게임은 서로 다른 컴퓨터 상에서 서로 다른 프레임율로 작동하며, 회전의 부드러움에 영향을 줄 수 있기 때문이다. 이때가 쿼터니온을 사용할 적절한 시점이며, 이 기법은 우리의 회전문과 같은 오브젝트의 단순한 회전을 표현하기 위해서 단지 두/세개의 방향만을 요구한다. 또한 당신은 개별적인 프레임율에 따라서 보간되는 위치의 개수를 동적으로 조정할 수도 있다.
회전은 단지 x, y, z 좌표축을 중심으로 한 세 개의 자유각(degrees of freedom, DOF)만을 포함한다. 그러나 9 DOF(3x3 행렬이라 가정)는 회전에 제약을 가할 것을 요구한다 - 우리가 필요로하는 것보다 더 많이. 또한 행렬은 "미끄러지는(drifting)" 경향이 있다. 이것은 6개의 제약(constraint) 중 하나가 위반되고 행렬이 상대적인 축을 중심으로 한 회전을 설명할 때 이러한 상황이 발생한다.
이 문제와 싸우는 것은 행렬 직교화(orthonormalized)를 유지할 것을 요구한다 - 그것은 제약에 순종하는 것을 보증한다. 그러나 이렇게 하는 것은 계산적인 낭비이다. 행렬 미끄러짐을 해결하기 위한 일반적인 방법은 상대적인 기반(basis)를 직교 기반으로 변환하기 위한 Gram-Schmidt 알고리즘에 의존한다. Gram-Schmidt 알고리즘이나 행렬 미끄러짐을 해결하기 위해서 정확한 행렬을 계산하는 것은 많은 CPU 사이클을 소비하며, 부동 소수점 수학을 사용하고 있음에도 불구하고 매우 자주 수행되어야만 한다.
회전 행렬의 또 다른 단점은 그것들이 두 개의 방향 사이의 회전을 보간하기에는 사용하기 너무 어렵다는 것이다. 또한 결과 보간은 가시적으로 매우 변덕스러우며, 이것은 게임에 더 이상 적절치 않음을 의미한다. 오일러 각 표현은 매우 효율적이다. 왜냐하면 그것은 단지 세 개의 변수만을 사용해 세 개의 DOF 를 표현하기 때문이다. 또한 오일러 각은 모든 제약에 복종할 필요가 없으며, 결국 미끌어지는 경향도 없고 재조정될 필요도 없다.
예를 들어 비행 시뮬레이션에 의해서 연속되는 회전이 수행되었다고 상상해 보자. 당신은 첫 번째 회전을 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 문제를 제외하고는) 같은 문제를 내포한다 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 는 실제 값이다.) 나는 두 번째 공식을 이 기사 전반에서 사용할 것이다. 쿼터니온이 표현되는 방식에 대해서 알게 되었으니, 그것들을 사용하는 기본적인 연산을 배워보자.
모든 다른 연산들은 이들 기본적인 것들로부터 쉽게 이끌어내질 수 있으며, 그것들은 여기에 동봉된 라이브러리에서 세부적으로 설명해 놓았다. 나는 단지 단위 쿼터니온만을 다룰 것이다. 각 쿼터니온은 4D 공간으로 이동될 수 있으며(왜냐하면 각 쿼터니온은 네 개의 부분으로 구성되기 때문이다) 그 공간은 쿼터니온 공간이라고 불린다. 단위 쿼터니온은 그것들의 크기(magnitude)각 1이고 그것은 쿼터니온 공간의 하위 공간인 S3를 형성한다. 이 하위공간은 4D 구체로서 표현될 수 있다. (그것들은 하나의 단위 법선을 가진다) 이것은 당신이 수행해야만 하는 필수 연산들의 개수를 감소시켜 준다.
오늘날 대부분 지원하는 Direct3D Immediate 모드(retained 모드는 쿼터니온 회전의 제한된 집합을 가진다) 와 OpenGL 의 API는 쿼터니온을 직접적으로 지원하지 않는다. 결과적으로 당신은 이 정보를 당신이 선호하는 API 에 넘기기 위해서 쿼터니온 방향을 변환할 필요가 있다. OpenGL 과 Direct3D 모두 당신에게 회전을 행렬로 지정하는 방법을 제공하며, 쿼터니온에서 행렬로 변환하는 루틴이 유용하다. 또한 당신이 회전을 일련의 쿼터니온으로서 저장하지 않는 (NewTek 의 LightWave 와 같은) 그래픽 패키지로부터 씬 정보를 import 하기를 원한다면, 당신은 쿼터니온 공간으로부터 혹은 공간으로 변환하는 방법을 필요로 하게 될 것이다.
쿼터니온을 사용해서 회전을 직접 지정하는 것은 어렵다. 당신의 캐릭터나 오브젝트의 방향을 오일러 각으로 젖아하고 그것을 보간 전에 쿼터니온으로 변환하는 것이 최선이다. 사용자의 입력을 받은 이후에 쿼터니온을 직접 재계산하는 것보다는 오일러 각을 사용해 회전을 각으로 증가시키는 것이 더 쉽다(즉 roll = roll + 1).
쿼너티온, 회전 행렬, 오일러 각 사이의 변환은 매우 자주 수행되기 때문에, 변환 과정을 최적화하는 것이 매우 중요하다. 단위 쿼터니온과 행렬 사이의 (9개의 곱셈만을 포함하는) 매우 빠른 변환은 Listing 2 에 나와 있다. 그 코드는 행렬이 오른손 좌표계에 있으며 행렬 회전이 열우선 순으로 표현된다고 가정한다는 데 주의하라(예를 들어 OpenGL 호환). MatToQuat(float m[4][4], QUAT * quat)
만약 당신이 단위 쿼터니온을 다루고 있지 않다면, 부가적인 곱셈 및 나눗셈이 요구된다. 오일러 각을 쿼터니온으로 변환하는 것은 Listing 3 에 나와 있다. 게임 프로그래머에 있어서 쿼터니온의 가장 강력한 장점 중 하나는 두 개의 쿼터니온 방향 사이의 보간이 매우 쉬우며 부드러운 애니메이션을 생성할 수 있다는 것이다. 이게 왜 그런지 설명하기 위해서 구체 회전을 사용한 예제를 살펴 보자. 구체 회전 보간은 4 차원에서 최단 경로(arc)인 단위 쿼터니온 구체를 따른다. 4D 구체는 상상하기가 어렵다. 나는 3D 구체(Figure 3)를 사용해 쿼터니온 회전과 보간을 가시화하도록 하겠다.
쿼터니온은 회전 혼합시에 요구되는 계산을 단순화한다. 예를 들어 당신이 행렬로서 표현되는 두 개 이상의 방향을 가지고 있다면, 두 개의 중간 회전을 곱함으로써 그것들을 쉽게 결합할 수 있다.
QuatToMatrix(QUAT * quat, float m[4][4]){
}
이 결합(composition)은 27 개의 곱셈과 18 개의 덧셈을 포함하며, 3x3 행렬로 간주한다. 다시 말해 쿼터니온 결합은 다음과 같이 표현될 수 있다.
이제 효율적인 곱하기 루틴을 가지게 되었다. 가장 짧은 호를 따라서 두 쿼터니온 회전을 보간하는 방법에 대해서 살펴 보자. 구형 선형 보간(Spherical Linear intERPolation(SLERP)) 가 이를 수행하며 다음과 같이 작성될 수 있다.
여기에서 pq = cos(q) 와 인자 t 는 0 부터 1 사이의 값이다. 이 공식의 구현은 Listing 5 에 제출되어 있다. 만약 두 개의 방향이 너무 가깝다면, 당신은 0 으로 나누는 것을 막기 위해서 선형 보간을 사용할 수 있다.
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)]으로서 표현될 수 있다. 이것은 첫 번째 방향이 항상 절대적인 것임을 의미한다. 왜냐하면
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)을 변경하도록 구현할 수도 있다.
당신은 카메라의 회전 중심(pivot point) 을 그것이 따라 움직이는 오브젝트의 중심으로 설정할 수 있다. 이것은 당신이 캐릭터가 게임 월드 안에서 이동할 때 이미 만들어낸 계산을 이용할 수 있도록 해 준다.
1인칭 액션 게임에서는 쿼터니온 보간을 이용하지 말라고 하고 싶다. 왜냐하면 이들 게임은 일반적으로 플레이어의 행동에 대한 즉각적인 반응을 요구하며, 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)은 다음과 같은 미분방정식으로 표현될 수 있다.
여기에서 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 튜토리얼을 찾아보기 바란다. 쿼터니온은 회전을 저장하고 수행하기 위한 매우 효율적이며 극단적으로 유용한 기법이다. 그리고 그것들은 다른 메서드들에 비해서 많은 이점을 제공한다. 불운하게도 그것들은 가시화될 수 없으며, 매우 직관적이지 못하다. 그러나 만약 당신이 내부적으로는 쿼터니온을 사용해 회전을 표현하고, 직접 표현으로서 약간의 다른 메서드들(예를 들어 각-축 또는 오일러각) 사용한다면, 당신은 그것들을 가시화할 필요가 없어질 것이다. [출처] 쿼터니온을 사용해 오브젝트 회전시키기|작성자 라이푸 |
투영행렬로 변환 한 후 다시 카메라 공간의 위치,벡터(norm) 을 구하는 방법과 d3dx9math.h (0) | 2013.07.09 |
---|---|
점,벡터 구분의 미묘함 (0) | 2013.05.08 |
Catmull-Rom 스플라인 곡선 D3DXVec3CatmullRom (0) | 2013.03.21 |
Fast Overlap Test for OBBs (0) | 2013.03.20 |
Object Oriented Bounding Box 를 이용한 Collision Detection (0) | 2013.03.18 |
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 곡선 처럼 해서 풀수 있다
이것을 위에서 설명한 Catmull- Rom 매개변수 방정식에 대입한다
(위의 식과는 약간 변형된 형태)
가 된다. 이걸로 t를 0부터 1까지 움직이면 BC 간에 곡선을 그린다
실제로 그래프에 그려보면 아래와 같이 된다
현재 DirectX 에서는 아래와 같은 함수를 제공하고있다
D3DXVec2CatmullRom(D3DXVECTOR2* pOut, 위 함수들은 각각 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
점,벡터 구분의 미묘함 (0) | 2013.05.08 |
---|---|
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
Fast Overlap Test for OBBs (0) | 2013.03.20 |
Object Oriented Bounding Box 를 이용한 Collision Detection (0) | 2013.03.18 |
Simple cloud layer SceneNode (0) | 2013.03.05 |
쿼터니온을 사용해 오브젝트 회전시키기 (0) | 2013.04.24 |
---|---|
Catmull-Rom 스플라인 곡선 D3DXVec3CatmullRom (0) | 2013.03.21 |
Object Oriented Bounding Box 를 이용한 Collision Detection (0) | 2013.03.18 |
Simple cloud layer SceneNode (0) | 2013.03.05 |
근사 편미분 파동방정식, 경계 범위 식구하기(2) (0) | 2013.02.24 |
http://www.gingaminga.com/Data/Note/oriented_bounding_boxes/
Object Oriented Bounding Box 를 이용한 Collision Detection
96419-044
출처; http://mimosa.snu.ac.kr/~rabbit2/
<목차>
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를 잡기 때문에, 직육면체 모양의 물체를 가장 효율적으로 모델링할 수 있다. |
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 분리되어 있기 위한 필요충분 조건은 다음과 같다:
위의 식에서 첫 번째 summation의 두 번째 항은 다음과 같이 단순화 된다.
<실행방법>
그냥 가만히 실행시키고 기다리고 있으면 차가 건물에 가서 쿵.. 하고 박는다 -_- 화살표로 시점을 약간 변화시킬 수 있다.
<코드설명>
CBox 클래스
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 |
int BoxBoxIntersectionTest(const CBox& box0,const CBox& box1)
box0 와 box1이 서로 교차하는지 검사를 해서 교차를 하면 1, 그렇지 않으면 0을 리턴한다. 함수의 동작방식은 위의 표에 있는 15개의 seperating axis를 하나씩 검사하는 방식이다. 주석을 보고 표를 참고하면 이해하는데 아무런 어려움이 없을 것이다.
D3DXMATRIX* GetBoxTransform(D3DXMATRIX *pMat, CBox* pBox)
pBox의 transform을 D3DXMATRIX 형태로 변환시켜 주는 일종의 wrapper 함수이다.
void SetBoxTransform(const D3DXMATRIX* pMat, CBox* pBox)
위 함수와 쌍을 이루는, 이번에는 pMat의 transform을 pBox의 axis 벡터로 변환을 해 주는 함수이다.
void initBox(CBox *pBox, const D3DXVECTOR3& vecMin, const D3DXVECTOR3& vecMax)
Mesh로부터 구해 온 bounding box의 minimum 좌표, maximum 좌표를 받아서 pBox의 center와 extent를 계산하고 axis를 초기화한다.
void moveBox(CBox *pBox, const D3DXMATRIX& mat)
pBox를 mat에 의해 움직인다. 일단 3개의 axis들을 변환한 이후에 center를 변화하는 방식으로 되어 있다.
이 함수들을 이용해서 코드는 다음 순서로 동작하면 된다:
5. 웹페이지 다운로드
Catmull-Rom 스플라인 곡선 D3DXVec3CatmullRom (0) | 2013.03.21 |
---|---|
Fast Overlap Test for OBBs (0) | 2013.03.20 |
Simple cloud layer SceneNode (0) | 2013.03.05 |
근사 편미분 파동방정식, 경계 범위 식구하기(2) (0) | 2013.02.24 |
근사 편미분 파동방정식, 경계 범위 식구하기 [구현] (0) | 2013.02.22 |
http://irrlicht.sourceforge.net/forum//viewtopic.php?t=43873#p250538
Fast Overlap Test for OBBs (0) | 2013.03.20 |
---|---|
Object Oriented Bounding Box 를 이용한 Collision Detection (0) | 2013.03.18 |
근사 편미분 파동방정식, 경계 범위 식구하기(2) (0) | 2013.02.24 |
근사 편미분 파동방정식, 경계 범위 식구하기 [구현] (0) | 2013.02.22 |
근사 편미분 파동방정식, 경계 범위 식구하기 (0) | 2013.02.22 |
-1< ( (2-(8*c^2*t^2)/d^2 +u*t ) )/(u*t+2) <1, t>0 , c>0 , d > 0 for t
Object Oriented Bounding Box 를 이용한 Collision Detection (0) | 2013.03.18 |
---|---|
Simple cloud layer SceneNode (0) | 2013.03.05 |
근사 편미분 파동방정식, 경계 범위 식구하기 [구현] (0) | 2013.02.22 |
근사 편미분 파동방정식, 경계 범위 식구하기 (0) | 2013.02.22 |
행렬에 정규직교 단위벡터3개를 세팅하는 방법과 활용 (0) | 2013.01.11 |
....
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;
Simple cloud layer SceneNode (0) | 2013.03.05 |
---|---|
근사 편미분 파동방정식, 경계 범위 식구하기(2) (0) | 2013.02.24 |
근사 편미분 파동방정식, 경계 범위 식구하기 (0) | 2013.02.22 |
행렬에 정규직교 단위벡터3개를 세팅하는 방법과 활용 (0) | 2013.01.11 |
기초 : 행기준 행렬과 열기준 행렬 (0) | 2013.01.09 |
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
근사 편미분 파동방정식, 경계 범위 식구하기(2) (0) | 2013.02.24 |
---|---|
근사 편미분 파동방정식, 경계 범위 식구하기 [구현] (0) | 2013.02.22 |
행렬에 정규직교 단위벡터3개를 세팅하는 방법과 활용 (0) | 2013.01.11 |
기초 : 행기준 행렬과 열기준 행렬 (0) | 2013.01.09 |
기초 : 오브젝트 법선벡터 추리기 : Backface Culling (0) | 2013.01.01 |
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 기초적인것이지만 이 활용도는 생각외로 큽니다
근사 편미분 파동방정식, 경계 범위 식구하기 [구현] (0) | 2013.02.22 |
---|---|
근사 편미분 파동방정식, 경계 범위 식구하기 (0) | 2013.02.22 |
기초 : 행기준 행렬과 열기준 행렬 (0) | 2013.01.09 |
기초 : 오브젝트 법선벡터 추리기 : Backface Culling (0) | 2013.01.01 |
카메라 클래스 - 쿼터니온 (0) | 2012.11.18 |
http://blog.naver.com/oranke?Redirect=Log&logNo=40000788158 에서 일부 내용을 추려옵니다
뷰변환 행렬에 대해 살펴보기 전에 짚고 넘어가야 할 문제가 있습니다. OpenGL과 D3D를 말할 때 가장 큰 차이점으로 오른손 좌표계냐 왼 손 좌표계냐를 따집니다. 그리고 또 하나 얘기하는 것이 바로 행기준 행렬이냐 열기준 행렬이냐는 것이죠...
좌표계 문제는 Z좌표를 뒤집기만 하면 끝납니다. 앞에서 살펴봤던 회전행렬 역시 오른손 좌표계가 오른손 법칙으로 양의 방향을 규정하는 것 처럼 왼손 좌표계는 왼손이 감기는 방향을 양의 방향으로 하기 때문에 회전행렬을 그대로 쓸 수 있습니다. 양의 방향에 해당하는 좌표평면과 축을 2차원에 설정해 그려보면 금방 알 수 있습죠...
[출처] [산수 02] 행기준 행렬과 열기준 행렬 |작성자 오랑캐꽃
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 |
[출처] [산수 02] 행기준 행렬과 열기준 행렬 |작성자 오랑캐꽃
행기준 변환행렬 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에서는 ① 에서의 방법으로 행렬을 표시하고 곱해주네요
근사 편미분 파동방정식, 경계 범위 식구하기 (0) | 2013.02.22 |
---|---|
행렬에 정규직교 단위벡터3개를 세팅하는 방법과 활용 (0) | 2013.01.11 |
기초 : 오브젝트 법선벡터 추리기 : Backface Culling (0) | 2013.01.01 |
카메라 클래스 - 쿼터니온 (0) | 2012.11.18 |
두개의 복소수 곱 => 두 복소수의 크기만큼이 각 두 복소수의 회전 만큼이 더해진각도가 회전된 것 (0) | 2012.11.02 |
http://www.gpgstudy.com/forum/viewtopic.php?topic=7585
Backface Culling에 대한 설명이 좀 부족한듯 싶어서, 설명을 추가해봅니다.
후면 추리기를 할때 삼각형의 앞면이 어느 방향인지를 알아야 합니다.
삼각형이 V0, V1, V2 3개의 정점으로 이루어 져있을때,
삼각형의 법선 벡터를 일반적으로 (V1 - V0) X (V2 - V1) 이렇게 구합니다.
이 삼각형의 법선 벡터와 카메라 벡터의 사이각이
90보다 클때 삼각형이 보인다고 판단합니다. (내적의 값이 0보다 작을때)
그런데 외적의 결과가 왼손 좌표계와 오른손 좌표계에서 서로 반대입니다.
(Direct3D는 왼손 좌표게를 OpenGL은 오른손 좌표계를 씁니다.)
삼각형의 법선 벡터가 카메라 벡터와 반대방향이 되면
왼손 좌표게에서는 카메라에서 봤을때 정점들의 순서가 시계방향이 되고,
오른손 좌표계에서는 정점들의 순서가 반시계방향이 됩니다.
행렬에 정규직교 단위벡터3개를 세팅하는 방법과 활용 (0) | 2013.01.11 |
---|---|
기초 : 행기준 행렬과 열기준 행렬 (0) | 2013.01.09 |
카메라 클래스 - 쿼터니온 (0) | 2012.11.18 |
두개의 복소수 곱 => 두 복소수의 크기만큼이 각 두 복소수의 회전 만큼이 더해진각도가 회전된 것 (0) | 2012.11.02 |
강체의 방향을 나타낼 수 있는 행렬[물체의 각속도, 오일러 각(Euler Angles), 회전행렬의 관계] PDF (0) | 2012.11.02 |
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는 동일하다.
기초 : 행기준 행렬과 열기준 행렬 (0) | 2013.01.09 |
---|---|
기초 : 오브젝트 법선벡터 추리기 : Backface Culling (0) | 2013.01.01 |
두개의 복소수 곱 => 두 복소수의 크기만큼이 각 두 복소수의 회전 만큼이 더해진각도가 회전된 것 (0) | 2012.11.02 |
강체의 방향을 나타낼 수 있는 행렬[물체의 각속도, 오일러 각(Euler Angles), 회전행렬의 관계] PDF (0) | 2012.11.02 |
자동차, 비행기를 목표하는곳까의 경로 생성 (0) | 2012.11.02 |
저번에서 얘기 했듯이 이번에는 복소평면에서의 복소수의 성질에 대해 알아보자
위에 나오는 평면을 복소평면이라 하는데, 복소평면이란 가로축을 실수축, 세로축을 허수축으로하는 평면을 말한다.
처음에는 항상 그랫다 싶이 사람들은 허수를 믿지 않았다. 왜냐면 볼 수 없었기 때문이다. 실제로 사람들이 음수를 인정하지 않던 이유도 '-3개의 사과','-5ㅡm의 막대'를 볼 수 없었기 때문이었다. 그래서 덴마크의 측량기사 카스파르 베셀(1745~1818)은
이렇게 생각했다.
"허수는 수직선의 어디에도 없다. 그렇다면 수직선의 밖, 즉 원점에서 위의 방향으로 뻗은 화살표를 허수로 생각하면 되지않을까?"
이 방법을 이용하면 드디어 허수가 "눈에 보이게" 된다.
이러한 아이디어는 비슷한 시기에 프랑스의 회계사 장 로베르 아르강(1768~1822)와 독일의 수학자 카를 프리드리히 가우스 (1777~1855)에 의해서도 발표되었다. 그래서 복소평면을 가우스 평면이라고도 한다.
설명은 이정도로하고 복소수의 기하학적의미에 대해 파악해보도록 하자.
일단 복소수의 사칙연산이다.
위의 그림은 복소수의 덧셈을 표기한 것이다. 고2학생들중 기학벡터를 배운 학생 또는 그 이상의 교육을 받은 사람이면 어디서 많이 본듯한 그림일 것 같다. 복소수의 덧셈은 마치 벡터의 합을 구하는 것과 매우 흡사하다.
합을 눈으로 봤으니 이제는 곱을 눈으로 봐보자.
곱을 보기 전에 일단 허수단위 i의 순환을 눈으로 보고 곱으로 들어가도록 하자.
위의 순환 성질과 밑의 그림의 관련성이 보이는가?
허수에 대해 잘 배운 사람이면 보일지도 모르겠다. 허수의 곱셈은 회전의 의미 가 있다.
위의 경우에는 i를 곱함으로써 점이 90도 회전하는 걸 볼 수 있다.
이를 다른 일반적인 복소수에 대해서도 일반화를 시키자면
위에서 보면 복소수 a+bi 와 c+di 를 곱하고 있다. 이것을 잘 한 번 살펴보자
위에서 과 는 각각의 복소수의 절댓값으로서 원점과의 거리를 의미하고 과 는 실수축의 양의방향과 이루는 각도이다.
이제 (a+bi)(c+di) 를 해보자
위 두식을 비교해 보면 알 수 있듯이 두 복소수의 곱의 절댓값은 두 절댓값의 곱과 같다는 것을 알 수 있다.
이번에는 각의 관점에서 복소수의 곱을 봐보자
두 복소수를 위와 같이 놓고 곱을 해보자
위 식으로 알 수 있듯이 두 복소수의 곱이 이루는 각은 두 복소수의 각을 더한 것과 같아진다.
(만약 위식이 이해가 가지 않으면 삼각함수의 덧셈정리라고 검색을 한번 해보시길)
이런식으로 복소수의 곱을 해보면 쉽게 회전 확대의 원리를 이해 할 수 있을 것이다.
오늘은 여기 까지만 하고 다음번 복소수에서는 이런 복소수가 도데체 어디에 쓰이는 지 한번 알아보도록 하자.
[출처] 복소수, 허수에 대해 2|작성자 jini
기초 : 오브젝트 법선벡터 추리기 : Backface Culling (0) | 2013.01.01 |
---|---|
카메라 클래스 - 쿼터니온 (0) | 2012.11.18 |
강체의 방향을 나타낼 수 있는 행렬[물체의 각속도, 오일러 각(Euler Angles), 회전행렬의 관계] PDF (0) | 2012.11.02 |
자동차, 비행기를 목표하는곳까의 경로 생성 (0) | 2012.11.02 |
Z*X*Y 행렬 곱과 회전 사원수곱 (0) | 2012.11.02 |
pdf 자료
카메라 클래스 - 쿼터니온 (0) | 2012.11.18 |
---|---|
두개의 복소수 곱 => 두 복소수의 크기만큼이 각 두 복소수의 회전 만큼이 더해진각도가 회전된 것 (0) | 2012.11.02 |
자동차, 비행기를 목표하는곳까의 경로 생성 (0) | 2012.11.02 |
Z*X*Y 행렬 곱과 회전 사원수곱 (0) | 2012.11.02 |
구면선형보간의 핵심요소 (0) | 2012.11.02 |
두개의 복소수 곱 => 두 복소수의 크기만큼이 각 두 복소수의 회전 만큼이 더해진각도가 회전된 것 (0) | 2012.11.02 |
---|---|
강체의 방향을 나타낼 수 있는 행렬[물체의 각속도, 오일러 각(Euler Angles), 회전행렬의 관계] PDF (0) | 2012.11.02 |
Z*X*Y 행렬 곱과 회전 사원수곱 (0) | 2012.11.02 |
구면선형보간의 핵심요소 (0) | 2012.11.02 |
d3dx 수학 함수들을 opengl 에서 쓰고 싶을때 (0) | 2012.11.02 |
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}}
강체의 방향을 나타낼 수 있는 행렬[물체의 각속도, 오일러 각(Euler Angles), 회전행렬의 관계] PDF (0) | 2012.11.02 |
---|---|
자동차, 비행기를 목표하는곳까의 경로 생성 (0) | 2012.11.02 |
구면선형보간의 핵심요소 (0) | 2012.11.02 |
d3dx 수학 함수들을 opengl 에서 쓰고 싶을때 (0) | 2012.11.02 |
왼손좌표계와 오른손좌표계 때문에 행렬이 전치관계때문이 아니다 (0) | 2012.11.02 |
3D그래픽스 물리 수학, 프로그래밍 GPU Shader 게임엔진 알고리즘 디자인패턴 matlab etc.
구면선형보간함수를 구현하다보면 일반적인 벡터부분만 뽑아와 내적을 하는것이 DirectX 에서 제공하는 함수와 결과가
한번씩 반대로 나온다는것을 알 수 있다
그것을 아래 첨부한 내용과 같이 해결하면 된다
http://www.gpgstudy.com/forum/viewtopic.php?t=1949
내적이 0보다 작은 경우에 두 쿼터니온 중 하나의 부호와 내적결과의 부호를 반대로 해야한다
D3DX 의 slerp 함수는 이처럼 되어있음
자동차, 비행기를 목표하는곳까의 경로 생성 (0) | 2012.11.02 |
---|---|
Z*X*Y 행렬 곱과 회전 사원수곱 (0) | 2012.11.02 |
d3dx 수학 함수들을 opengl 에서 쓰고 싶을때 (0) | 2012.11.02 |
왼손좌표계와 오른손좌표계 때문에 행렬이 전치관계때문이 아니다 (0) | 2012.11.02 |
짐벌락 피하는 방법 (0) | 2012.11.02 |
http://www.gpgstudy.com/forum/viewtopic.php?p=121140
Re: D3DXMatrixTranspose용도는? |
올려짐: 2009-06-26
01:54 | ||
| |||
openGL의 경우 오른손 좌표계고 DirectX의 경우 왼손 좌표계 입니다. openGL을 쓰다가 D3DX수학함수들이 쓰고 싶을때.. D3DX 함수로 계산한 행렬을 openGL에 적용시킬때는 D3DXMatrixTranspose해서 넘겨줘야겠죠. 그리고 쉐이더에서 전치행렬이 필요한경우가 있습니다. 이때도 Transpose를 쓰죠. |
Z*X*Y 행렬 곱과 회전 사원수곱 (0) | 2012.11.02 |
---|---|
구면선형보간의 핵심요소 (0) | 2012.11.02 |
왼손좌표계와 오른손좌표계 때문에 행렬이 전치관계때문이 아니다 (0) | 2012.11.02 |
짐벌락 피하는 방법 (0) | 2012.11.02 |
쿼터니언 (0) | 2012.11.02 |
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)을 사용하는 것으로 알고
있습니다.
구면선형보간의 핵심요소 (0) | 2012.11.02 |
---|---|
d3dx 수학 함수들을 opengl 에서 쓰고 싶을때 (0) | 2012.11.02 |
짐벌락 피하는 방법 (0) | 2012.11.02 |
쿼터니언 (0) | 2012.11.02 |
광선추적 (Ray Tracing) (0) | 2012.11.02 |
2012/08/30 13:15 |
아래 내용은 위 내용에 조금 추가하면
위의 이미지는 x 축을 회전 시키는 과정이고 아래 그림은 x 축을 90 또는 -90 도로 회전시켰을때 이고
이때 y축과 z 축이 같은 회전 선상에 있다는 것을 알 수 있다
이때 짐벌락이 발생한다
d3dx 수학 함수들을 opengl 에서 쓰고 싶을때 (0) | 2012.11.02 |
---|---|
왼손좌표계와 오른손좌표계 때문에 행렬이 전치관계때문이 아니다 (0) | 2012.11.02 |
쿼터니언 (0) | 2012.11.02 |
광선추적 (Ray Tracing) (0) | 2012.11.02 |
크래머 공식 (0) | 2012.11.02 |
저는 쿼터니언이라는 용어가 마치 비밀스러운 어둠의 힘을 가지고 있는 암흑물체에 관한 양자론 용어처럼 느껴집니다. 여러분도 역시 이 어둠의 힘에 매료되었다면 이 글이 도움이 될 것입니다(그렇게 되기를 바랍니다). 이 글은 쿼터니언을 이용해서 회전을 하는 방법을 보여주어 쿼터니언을 좀더 쉽게 이해하는데 도움을 줄 것입니다. 만약 여러분이 글에서 잘못된 내용을 발견하시면 robin@cyberversion.com로 메일을 보내주세요. 또, 이 기사를 여러분의 사이트에 실으려고 하신다면 저에게 메일을 보내주세요. 저는 저의 글이 어디에 퍼져있는지 알고 싶답니다.
이 질문에 대답하기 위해서 우선 방향을 나타내는 일반적인 방법에 대해 논의해 보도록 합시다.
이것이 현재까지 알려진 방법중 방향을 나타내는 가장 간단한 방법입니다. 오일러 표현은 각각의 축마다 축주위의 회전량을 가지고 있습니다. 따라서, 다음과 같은 0도에서 360도(혹은 0~2π)의 범위에서 변하는 3개의 값을 가집니다.
x, y, z <-- 전역 좌표축 주위의 회전 각도
이 값은 각각 롤, 피치, 요(혹은 피치, 롤, 요, 등 등)를 표현합니다. 방향은 3개의 각도로부터 생성되는 3개의 회전 행렬을 지정한 순서대로 곱해서 구할수 있습니다.
Note: 회전은 전역 좌표계의 좌표축을 기준으로 회전합니다. 즉 최초의 회전이 그 후의 2번째 3번째의 회전축에 영향을 주지 않습니다. 이 때문에 짐벌락(gimbal lock) 문제가 발생하게 됩니다. 짐벌락에 대해서는 조금 후에 자세히 설명하겠습니다.
이 방법은 짐벌락을 피할수 있으므로 오일러각 방법보다 좀더 낫습니다. 회전축과 각도에 의한 표현은 임의의 회전축을 나타내는 단위 벡터와 단위 벡터 주위의 회전을 나타내는(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 || = 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) | // 오일러를 쿼터니언으로 변환 |
위의 표현은 모두 같습니다. 제가 말할려고 하는 것은 방향에 쿼터니언을 사용하는 것은 오일러나 회전축과 각도에 의한 표현과 완전히 같고, 앞서 언급한 변환 함수를 통해 상호교환이 가능하다라는 것입니다.
다만, 위의 쿼터니언에 의한 표현에는 오일러 표현법과 마찬가지로 짐벌락의 위험성이 있습니다.
역자주 – "왜 짐벌락 위험성이 있지"라는 답을 글내용상 알수가 없습니다.
물론, 아직은 회전을 쿼터니언으로 만드는 방법을 모르시겠지만 그것은 아래 부분에서 설명하겠습니다.
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개의 쿼터니언을 합성하는 실수를 범한 적이 있고, 이 때 그 결과 쿼터니언이 특정 각도에서만 실패하는 이유를 알아내기 위해 많은 시간을 고민했습니다.
이제 다른 표현법을 쿼터니언으로 변환하는 방법을 배워봅시다. 저는 샘플 프로그램에 있는 모든 변환들을 사용하지는 않지만 역운동학 등의 보다 진보된 용도로 쿼터니언 방향을 사용하려고 할 때 이것들이 필요할 것입니다.
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이 다르게 표현됩니다
드디어 "어떻게 쿼터니언이 짐벌락을 피할수 있지?"에 대해서 모두가 기다렸던 궁금증에 대답할 때가 왔습니다.
기본적인 아이디어는
우선, 저는 샘플 코드에 대해서는 어떠한 책임도 지지 않겠습니다. 이 코드는 난잡스러고 제대로 구성되어 있지도 않습니다. 이것은 쿼터니언 테스트할 때 제 프로그램에서 사용했던 쿼터니언 코드를 간략히 줄여놓은 버전일 뿐입니다. 따라서 코드에 대해서 크게 신경을 쓰지 않았다는 점만 기억해 주셨으면 좋겠습니다(돈받고 파는 코드가 아니니까요 ^^)
저는 2개의 실행 가능한 샘플을 준비했습니다. 첫번째 프로그램인CameraEuler.exe (http://www.gamedev.net/reference/programming/features/qpowers/CameraEuler.zip)는 오일러 각을 사용해서 카메라를 구현한 예입니다.
여러분이 주의깊게 봐야 할 부분은 main.cpp의 Main_Lopp 함수입니다.
여러분이 while 문에서 눈여겨 봐야할 곳은 다음과 같습니다.
위/아래 화살표 키는 X축의 회전, 왼쪽/오른쪽 화살표 키는 Y축회전, Insert/PageUp 키는 Z축회전을 담당합니다.
이 프로그램은, 짐벌락을 일으킵니다. 여러분이 짐벌락 현상을 보고 싶으면, 요를 90도로 취하고 X축이나 Z축회전을 시도해 보십시오. 그리고 무슨일이 일어나는지 살펴보세요.
지금부터는 쿼터니언으로 짐벌락 문제를 해결한 프로그램을 살펴봅시다. 이 프로그램은CameraQuat.exe (http://www.gamedev.net/reference/programming/features/qpowers/CameraQuat.zip)이며 앞의 프로그램을 조금 변경한 프로그램입니다.
여러분이 while문에서 눈여겨봐야하는 곳은 다음과 같습니다.
키가 눌러졌을 때, 저는 키 입력에 대응하는 고유의 축에서의 작은 회전을 나타내는 임시 쿼터니언을 생성했습니다. 그리고 임시 쿼터니언과 카메라 쿼터니언을 곱했습니다. 이 4차원 공간에서의 회전을 합성한 것이 짐벌락을 피하게 해줍니다. 여러분이 직접 이것을 해보고 자신의 눈으로 확인해주세요.
카메라 쿼터니언은 최종 변환 행렬로 합성할 수 있도록 행렬이나 그에 상응하는 형태로 변환되야 합니다. 여러분이 쿼터니언을 사용할 때, 3차원 공간과 4차원 공간은 섞일 수가 없기 때문에 항상 이 작업을 해주셔야 합니다. OpenGL의 경우에 저는 그냥 쿼터니언으로부터 회전축을 변경하기만 했고 나머지는 API에게 위임했습니다.
제가 두번째 프로그램에서 오일러 각을 회전에 사용하지 않았지만 저는 여러분이 첫번째 프로그램에서의 오일러 회전에 대해서 볼 수 있게 하기 위해서 오일러 회전 부분을 남겨 두었습니다. 오일러 각은 하나 이상의 축으로 회전시키면 올바르게 되지 않을 것입니다. 왜냐하면 쿼터니언이 카메라 쿼터니언으로부터 오일러 각을 얻는 대신 키 입력을 통해서 계산하기 때문입니다. 두번째 프로그램은 프로그램을 시작했을 때 그냥 여러분이 요 값을 90도로 회전하려는 경우에 짐벌락이 더 이상 일어나지 않는다는 것을 보기 위해서 만든 참고자료일뿐입니다.
Note: 저는 여러분에게 저의 수학 라이브러리를 사용하라고 추천하고 싶지 않습니다. 여러분이 쿼터니언을 이해하고 나서 스스로 짜 보십시오. 참고로 저는 이 코드를 전부 버리고 다시 만들 예정입니다. 이 코드들은 저에게 있어서 너무나 지저분하고 난잡한 코드입니다.
여러분이 이미 눈치채셔겠지만 저는 쿼터니언을 오일러 각으로 변환하는 방법에 대해서는 설명하지 않았습니다. 그 이유는 아직까지도 완벽하게 동작하는 변환을 제가 알아내지 못했기 때문입니다. 제가 알고 있는 유일한 방법은 쿼터니언으로부터 행렬을 얻고, 그 행렬로부터 오일러 각을 추출하는 것입니다. 그러나, 오일러에서 행렬 변환은 다대일 관계(sin과 cos으로 인해)이기 때문에 저는 atan2를 사용해서 그 역을 구하는 방법을 알지 못합니다. 만약 정확하게 행렬로부터 오일러 각을 추출하는 방법을 알고 계시다면 저에게 알려주세요.
제가 보여드리지 않은 다른 내용은 행렬을 쿼터니언으로 변환하는 방법입니다. 여러분이 오일러 표현과 회전축과 각도에 의한 표현을 쿼터니언으로 변환할 때 행렬을 거치지 않고도 직접변환이 가능하므로 굳이 행렬을 쿼터니언으로 변환할 필요가 없기 때문입니다.
여러분이 쿼터니언을 완전히 이해했다고 생각하신다면 다시 한번 생각해 보십시오. 아직도 쿼터니언에 대해서 더 배워야 할 게 남아 있습니다. 제가 전에 회전축과 각도(Axis Angle)에 의한 표현이 왜 나쁠까라고 말했던 것을 기억하시나요? '보간'이라는 단어가 갑자기 떠오르지 않나요?
저는 쿼터니언을 사용한 보간에 대해서 설명한 시간을 갖지 못했습니다. 이 글은 제가 예상한 것보다 시간이 오래 걸렸습니다. 여기서 SLERP(구면 선형 보간)에 대한 기본적인 아이디어를 드리겠습니다. 이것은 기본적으로 2개의 쿼터니언 방향 사이에 일련의 쿼터니언들을 생성합니다. 일련의 쿼터니언들은 처음과 마지막 쿼터니언사이에서 부드러운 모션(움직임)의 결과를 제공합니다. 오일러와 회전축 표현으로는 일관성있게 이러한 부드러운 움직임을 만들 수 없습니다.
저는 이 기사가 쿼터니언 이론 뒤에 숨겨진 알수 없는 미스테리들을 속시원하게 없애주었길 바랍니다. 다시한번 마지막으로 여러분에게 당부하고 싶은 말은 서로 다른 좌표계에서 2개의 쿼터니언을 곱하지 말아주십시오. 이렇게 하시면 여러분은 고통의 시간을 맛보시게 될것입니다.
그러면 새로 찾아낸 쿼터니언의 능력에 여러분이 기뻐하시길 바라면서 저는 이제 그만 여러분과 헤어질까 합니다. 몸조심하시구여. 그리구 다시 만나뵙기를 바라면서...
왼손좌표계와 오른손좌표계 때문에 행렬이 전치관계때문이 아니다 (0) | 2012.11.02 |
---|---|
짐벌락 피하는 방법 (0) | 2012.11.02 |
광선추적 (Ray Tracing) (0) | 2012.11.02 |
크래머 공식 (0) | 2012.11.02 |
행렬식 (0) | 2012.11.02 |
ttp://cs.sungshin.ac.kr/~hkim/LECTURE/CG/Shading/RayTracing.htm
|
|
|
|
|
|
|
그러나 위와 같은 방법으로는, 아무리 실감 나는 표현을 한다고 해도 거울이나 투명한 물체 등을 표현하기는 불가능하다. 그것은 지금까지의 표시 방법이 눈과 물체와의 위치관계에 따라 좌표를 변환하는 것을 기본으로 하고 있지만, 거울면에서는 다른 위치에서 바라본 듯한 풍경이 나타나기 때문이다. ray tracing 은 더욱 현실감 있는 표현이 가능방법으로서 광선추적이라고도 한다. 이것은 반사, 음영 만들기, 즉 유리 물체 등에 있어서의 빛의 굴절을 정확히 모사하고, 보다 현실에 가까운 입체표현이 가능한 방법으로 가장 유효한 랜더링 방법이다 Ray Tracing의 또다른 장점은 은면제거 알고리즘이 따로 필요하지 않다는 것이다. 그 이유는 가려진 물체가 발산하는 빛은 우리의 눈에 직접 들어오지 않고 다른 물체에 닿아버리므로 결국 이 빛은 화면에 표시되지 않기 때문이다. 그러나 광선을 하나씩 추적한다는 말에서 느낄 수 있듯이, 비록 광선추적 알고리즘 자체는 그리 복잡하지 않다 하더라도 전체적인 계산량이 많아진다는 단점이 있다.
Ray Tracing이란 관찰자의 눈에 들어오는 광선을 찾아 그 광선의 색을 화면에 표시해주는 방법으로서, 여러 방향에서 관찰자의 눈으로 들어오는 광선들의 색을 정확히 찾기만 한다면 영상을 만드는 일은 아주 간단해진다. 광선을 추적하는 방법에는 두 가지가 있는데, 하나는 광원이 내보내는 빛의 방향을 세분하여 추적하는 정방향 광선추적법이고, 다른 하나는 관찰자의 눈에서 광원방향으로 시선을 추적하는 역방향 광선추적법이다. (1) 정방향 광선추적법(Forward Ray Tracing) 정방향 광선추적법은 역방향 광선추적법에 비해 자연현상을 좀더 충실하게 시뮬레이션한다는 장점이 있기는 하지만, 광원이 내보내는 여러 방향의 광선을 모두 추적해야 하는 비능률적인 면이 있다. 즉, 우리가 수많은 방향으로 세분화시킨 광선들을 모두 추적한다 해도, 그것들 중 대부분은 우리의 눈에 들어오지 않을 뿐만 아니라 그러한 광선은 영상을 만들어내는 데 전혀 도움이 되지 않기 때문이다. 그러므로 충분한 개수의 광선이 눈(화면)에 들어오도록 만들려면 광원에서 출발한 광선들을 거의 무한하게 세분화시켜야 한다는 것을 알 수 있다. 그러나 광원을 떠나는 빛을 셀 수는 없는 일이므로, 사실 이러한 방법으로 자연현상을 흉내내는 일은 거의 불가능하다. (2) 역방향 광선추적법(Backward Ray Tracing) 관찰자의 시선방향을 따라가서 빛을 내보낸 물체를 찾아내는 방법이다. 시선이 물체에 닿았다는 것은 그 방향에서 물체의 빛이 눈으로 들어오는 것을 의미하므로, 시선방향으로 빛을 역추적하면 눈에 들어올 광선만을 추적할 수 있게 된다. 그러므로 광원에서 나오는 무한히 많은 빛들 중에서 눈에 들어올 빛을 찾아내는 정방향 광선추적법보다 시선방향으로 빛을 역추적하는 역방향 광선추적법이 훨씬 효율적임을 알 수 있다. 역방향 광선추적법이란 이러한 역추적 방법을 뜻하는 것으로서, 시선의 경로를 따라가다가 빛을 분산시키는 물체에 도달하면 Shading 모델로 광선의 색과 밝기를 결정하는 방법이다. 일반적으로 Ray Tracing이라 하면 광원에서 광선을 추적하는 정방향 광선추적법이 아닌 눈에서 광원으로의 시선추적 방법인 역방향 광선추적법을 의미한다.
눈에서 출발하는 시선을 설정하는 원리는 눈과 물체사이에 화면을 놓고 화소와 시선을 대응시키는 것이다. 눈에서 출발한 시선을 따라가다가 거울을 만나게 되면 시선을 반사시켜 방향을 바꾸어준 뒤, 시선과 만나는 다른 물체를 찾는 일을 되풀이한다. 그리고 헝겊과 같이 빛을 분산시키는 물체에 시선이 닿게 되면 더 이상 시선을 추적하기 어려우므로 헝겊의 색을 화소에 칠해주는데, 이 경우 단순히 한가지 색채만을 쓰는 것이 아니라 광원과 물체 표면과의 각도 관계를 이용하여 색에 밝기를 더해준다. Ray Tracing에서는 시선을 직선방정식으로 나타내고, 직선과 물체의 교점을 계산하는 방법을 사용하며, 여러 물체와의 교점 중에서 가장 가까운 교점을 시선이 도달한 곳으로 생각한다.
매끄러운 평면에 광선이 조사되면, 일부는 반사하고 일부는 내부를 향해 나아간다. 반사광선은 입사각 와 같은 각도로 반사하지만, 내부로 향하는 광선의 방향은 물체를 구성하는 물질의 굴절률에 따라 다르다. 표면에서 분리된 2개의 빛은 다시 다른 표면에 조사되어 분리된다. 아래 그림 (a)와 같이 다수의 반사, 굴절을 반복하여 시점으로 향한 빛이 실제로 관찰 되어진다. 즉, 시점에서 관측되어진 명도 I는 그림(b)의 나무로 표현되는 것과 같이, 반사광, S1과 물체 내부로부터의 투과광T1의 합으로 주어진다. 이 나무구조는 무한히 계속된다. 종래의 음영모델을 이용하는 경우, 광원으로부터 출발해서 반사, 분리의 반복을 계산하면 시접까지 도달한 빛은 극히 적고, 다른 많은 연산처리가 그래픽 표현에는 불필요하게 되고 만다. 따라서 레이트레이싱에서는 역으로 시점방향에서 출발해서 최종적으로 얻고 싶은 2차원 디스플레이상의 화살의 각 화소점만의 컬러정보를 얻는 수법을 이용하고 있다. 이상을 정리하면 다음과 같은 알고리즘이 되고 모든 것이 투명 물체인 경우의 기본적인 레이트레이싱법이 된다.
유리구 등과 같이 투명한 물체의 경우는 스넬의 법칙에 나타내듯이, 반사광 그 속으로 들어가는 투과광을 생각해서 임사되어진 단위광량 1을 적당한 비율로 반사광과 투과광에 분배한다. 그리고, 각각의 빛이 투명한 물체에 닿은 경우, 각각이 가진 광량에 비례해서 각 불투명 물체의 색을 혼합하고, 화소점의 컬러 정보로 한다. 혹시 두 개로 나뉘어진 빛이 다시 투명물체에 닿았을 때는 똑 같이 반복반사와 투과를 계산한다. 이렇게 해서 가지고 있는 광량이 어떤 명도 이하가 되든가, 반사의 횟수가 어떤 제한을 넘으면 계산을 그만두게 하면 된다. 이와 같은 계산을 화상평면상의 모든 화소점에 대해서 행하면 레이트레이싱 화상이 구해진다. 아래의 왼쪽그림은 렌즈 등의 투명한 유리구가 있는 경우의 레이트레이싱 화상 예이고, 아래의 오른쪽 그림은 경면반사가 있는 경우 화상의 예이다.
대상물체가 다음 식으로 나타내어지는 구체라고 하면 가 된다. 이식은 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%이상이 소요된다고 말하여 진다. 따라서 대상이 되는 입체를 내부에 포함하는 간단한 볼록입체를 이용하고, 이 입체를 새로이 볼록포라고 부르기도 한다. 볼록포와 광선의 교차를 우선 구하여 교차하지 않으면, 원래의 입체는 교차하지 않음을 확실히 알 수 있다.혹시 볼록포와 광선이 교차하고 있으면 실제로 대상물과 광선이 교차하는지의 여부를 종래대로 광선추적의 처리로 구한다. |
짐벌락 피하는 방법 (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)
쿼터니언 (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)라는 것으로 Aij = (-1)i+jMij로 표현
소행렬식 및 여인자 예)
가 주어졌을 때, 여인자 A12 와 A31은?
여인자 식에 대입하면 A12 = (-1)1+2M31와 A31 = (-1)3+1M31된다.
소행렬식 M12
소행렬식 M31
여인자 여인자
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;
}
동일한 행 성분(aj1, aj2, •••, ajn)에 대해서 표현
1X1정방 행렬 A=[a]의 행렬식 |A|=a
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);
}
광선추적 (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 |
크래머 공식 (0) | 2012.11.02 |
---|---|
행렬식 (0) | 2012.11.02 |
3차 곡선(Cubic polynomial curves ) (0) | 2012.11.02 |
곡선, SVG 곡선 사이트 링크들 (0) | 2012.11.02 |
[2D] 고속 선그리기, 브레슨햄 알고리즘 [영상강의 및 설명] (0) | 2012.11.02 |