** [강좌] Quaternion **
|
'수학 (Mathematics) > 3D수학' 카테고리의 다른 글
그래픽스에서 쓰이는 여러가지 수학적 기술들 설명 (0) | 2012.11.02 |
---|---|
3. 단위 쿼터니온과 임의축 회전의 대응 (0) | 2012.11.02 |
1. 복소수와 쿼터니온 그리고 회전 (0) | 2012.11.02 |
동차좌표 (0) | 2012.11.02 |
원근투형 TM (0) | 2012.11.02 |
** [강좌] Quaternion **
|
그래픽스에서 쓰이는 여러가지 수학적 기술들 설명 (0) | 2012.11.02 |
---|---|
3. 단위 쿼터니온과 임의축 회전의 대응 (0) | 2012.11.02 |
1. 복소수와 쿼터니온 그리고 회전 (0) | 2012.11.02 |
동차좌표 (0) | 2012.11.02 |
원근투형 TM (0) | 2012.11.02 |
** [강좌] Quaternion **
copyrightⓒ 김성완(kaswan) [1999년 6월 24일]
1. 복소수와 쿼터니온 그리고 회전쿼터니온과 3차원 회전의 연관성을 이해하려면 우선 쿼터니온의 할아버지인 복소수와 2차원 회전의 연관성을 먼저 살펴 보아야 합니다.
복소수(Complex number)야 여러분들이 이미 고등학교 수학에서 배웠으니 잘 아실 테고..
임의의 복소수 C = X + i*Y ( i² = -1 ) 가 있을때..
실수 성분 X와 허수 성분 Y를 각각 2차원 평면 좌표계의 x축 좌표와 y축 좌표로 대응시키면 임의의 실수를 수직선상에 표시할 수 있듯이 임의의 복소수를 2차원 평면상에 표시할 수 있지요.
이런 평면을 복소평면이라고 한다는 것도 배웠을 겁니다.
이때 x축을 실수축, y축을 허수축이라고 하지요.그럼 복소수와 2차원 회전의 연관성을 알아보지요.
복소평면상의 원점에서 1의 거리에 있는 모든 복소수들을 찍으면 반지름이 1인 단위원이 되죠.
실수축을 기준으로 반시계방향으로 잰 각도를 'θ'라고 하면 복소평면의 단위원상에 있는 복소수들은 R = cosθ + i*sinθ 로 나타낼 수 있습니다.자! 이제 임의의 복소수 C와 단위원을 나타내는 복소수 R을 서로 곱해봅시다.
복소수를 곱하는 법은 다들 아시죠?
(기억이 안나시면 고등학교 수학책 한번 들춰보세요!)
곱한 결과로 나온 복소수를 C'이라고하면C' = C*R
= X*cosθ - Y*sinθ + i*( X*sinθ + Y*cosθ )이걸 각 성분 별로 적어보면
X' = X*cosθ - Y*sinθ
Y' = X*sinθ + Y*cosθ그런데 이건 2차원 평면에서의 회전공식이랑 똑 같네요.
우리는 그냥 복소수끼리 곱하기만 한 건데..
결과는 2차원 행백터( x, y )에다 2차원 회전행렬을 곱한 거랑 똑같군요.| cosθ sinθ |
( x', y' ) = ( x , y ) * l l
| -sinθ cosθ |복소평면상에서의 의미를 살펴보면 임의의 복소수 C를 원점을 중심으로 θ 만큼 회전시킨 복소수가 바로 C' 이라는 것이죠.
여기서 우리가 알아낸 사실은 복소수의 곱이 의외로 2차원 회전과 연관되어있다는 겁니다.
그러면 복소수의 확장인 쿼터니온도 뭔가 회전과 관련성이 있을 것이라고 짐작할 수 있겠죠.
3. 단위 쿼터니온과 임의축 회전의 대응 (0) | 2012.11.02 |
---|---|
2. 쿼터니온의 유래와 Angular displacement (0) | 2012.11.02 |
동차좌표 (0) | 2012.11.02 |
원근투형 TM (0) | 2012.11.02 |
회전행렬 (0) | 2012.11.02 |
동차 좌표란 개념이 왜 생겼나?
V = a*Vx + b * Vy + c * Vz -> Vector V
P = r + a*Vx + b * Vy + c * Vz -> Vertex P
원점 r 에 관한 정보를 제외 시켜버리면 벡터 V 와 정점 P 는 모두 ( a, b, c ) 이므로
이 둘을 구분할수 없다. 이러한 불편을 해소시키기 위한것이 동차 좌표 개념이다.
이는 3차원 좌표를 3개의 요소로 표시할것이 아니라 차원을 하나 높여 4개의 요소로 표현하자는
것이다.
* 여기서 r 은 임의 위치에서 원점 을 가르키는 벡터다. 즉 원점이라 할수 있다.
V = a * Vx + b * Vy + c * Vz + 0 * r -> Vector V 는 ( a, b, c, 0 )
P = a * Vx + b * Vy + c * Vz + 1 * r -> Vertex P 는 ( a, b, c, 1 )
즉 마지막 요소 가 0 이면 Vector를 나타내고,
마지막 요소가 1이면 정점을 의미 하도록 한것이다.
이렇게 함으로써 Vector와 정점을 동일한 방법으로 표현할수 있다.
좀더 쉽게 접근하기 위해 차수를 내려 2차원 평면의 예를 들어보자.
2차원 평면상의 점 ( 1, 2 ) 를 동차 좌표로 표시하면 ( 1, 2, 1 ) 이 된다.
이는 3차원으로 말하면 Z 축으로 높이 1 의 위치에 있는 점에 해당된다.
엄밀히 말해 2차원 평면상의 점을 3차원 공간 상의 직선에 사상( mapping ) 시킨 것이 동차 좌표다.
다시 말해 원점에서 출발해 ( 1, 2, 1 )를 통과하는 모든 3차원 좌표가 동일한 2차원 좌표 ( 1, 2) 를
의미한다.
즉 ( 1, 2, 1 ) = ( 3, 6, 3 ) = ( 4, 8, 4 ) 이 된다.
따라서 동차 좌표의 마지막 요소로 앞 요소를 나눈 값이 실제 3차원 좌표가 된다.
같은 맥락에서 만일 3차원 동차 좌표를 4차원 ( x, y, z, w ) 좌표로 표시되면 3차원 실제 좌표는
( x / w, y / w, z / w )가 된다. 즉 ( 1, 2, 4, 1 ) = ( 2, 4, 8, 2 ) = ( 5, 10, 20, 5 ) 이며
w = 0 일 경우 그 자체가 Vector ( x, y, z ) 를 나타낸다.
출처 : http://blog.naver.com/kzh8055/140049456910
2. 쿼터니온의 유래와 Angular displacement (0) | 2012.11.02 |
---|---|
1. 복소수와 쿼터니온 그리고 회전 (0) | 2012.11.02 |
원근투형 TM (0) | 2012.11.02 |
회전행렬 (0) | 2012.11.02 |
회전 변환 (0) | 2012.11.02 |
http://egohim.blog.me/70000858060
월드 어딘가에 존재하는 x, y에 대한 x’, y` (위치에 대한 모니터상으로 변환 공식)
————————————————————————————————————————————
x` = ((x-left) 2/right-left) - 1 - 공식 x`
y’ = ((y-bottom) 2/top-bottom)) -1 - 공식 y`
공식 1과 공식 2는 3차원상의 어딘가의 점 x, y, z 에 대한
z = n (Z에 모니터위치 n) 의 어딘가에 찍히는 점 x`, y`, z` 의 공식이다.
x` (또는 y`) 는 -1 과 1사이 값이며, 곧 중심점 0을 기준으로 좌우측 또는 상하로
1만큼의 값의 x (또는 y)의 위치가 된다.
x’와 y` 구하는 공식 (원근이 가미된 투영 공식)
————————————————————————————————————————————
x, y, z를 기준으로 x`, y`, z` 각각의 기울기는 아래와 같다.
직선의 방정식 z = (p)z/(p)x * x 직선의 방정식 에서 z = n으로 대입
x = n(p)x / (p)z - 원근공식 x
역시 직선의 방정식 z = (p)z/(p)y*y 직선의 방정식에 z = n 대입
y = n(p)y / (p)z - 원근공식 y
시야절두체의 점은 left <= x <= right, bottom <= y <= top을 만족하므로
이 값을 -1, 1 사이의 값으로 변환 하려면
x` = ((x-left) 2/right-left) - 1(공식x`) 에 x = n(p)x / (p)z (원근공식 x) 를 대입한다.
x` = (( n(p)x / (p)z) -left) 2/right-left) - 1 인수분해
-> x` = 2n/right-left * ((p)x/(p)z) - (right+left/right-left) - 원근투영공식 x’
y’ = ((y-bottom) 2/top-bottom)) -1 (공식y`)에 y = n(p)y / (p)z (원근공식 y)를 대입한다.
y’ = (((n(p)y / (p)z)-bottom) 2/top-bottom)) -1 인수분해
-> y` = 2n/top-bottom * ((p)y/(p)z) - (top+bottom/top-bottom) - 원근투영공식 y’
x`는 즉 어딘가에 있는 점 x 를 원점을 기준으로 직선을 그엇을때
z = n 에 (모니터가 z축에 위치한 점 n에 해당 ,n은 near) 걸쳐지는 직선상의 점을 x`로 정의 한다.
y`는 즉 어딘가에 있는 점 y 를 원점을 기준으로 직선을 그엇을때
z = n 에 (모니터가 z축에 위치한 점 n에 해당 ,n은 near) 걸쳐지는 직선상의 점을 y`로 정의 한다.
z` 구하는 공식 (기울기가 가미)
————————————————————————————————————————————
z 는 near <= z <= far인데, 이 값을 0,1 사이의 값으로 변환 하여야 한다.
원근투영공식 x’ 와 원근투영공식 y’를 보면 z 값이 분모에 있으므로 아래와 같은 수식을 만족하는
A, B 항을 찾는다.
z` = A / z + B 공식 z`
near = 0 이며 z = n, z` = 0 을 대입하고, far = 1 이며 z = f, z` = 1 을 대입
0 = A/n + B - 공식near
1 = A/f + B - 공식far (->n = near, f = far)
공식near - 공식far = 1 이므로 정리 해보면
1 = A/f - A/n => 1 = A(1/f - 1/n) => 1 = A(n-f/fn) => A = fn / n-f
A = fn/n-f - 공식 z’A
0 = (fn /n - f) / n + B => 0 = fn / n(n-f) + B => B = f / f-n
B = f / f-n - 공식 z`B
공식 z`에 far의 A와 near의 B를 대입한다.
z` = (fn/n-f)(1/(p)z) + f/f-n
곧 원근투영 공식으로 어딘가의 점 z에 대한 모니터 화면 출력의 z`의 공식이 된다.
z` = (fn/n-f)(1/(p)z) + f/f-n - 원근투영공식 z`
동차 좌표계 (homegeneous Coordinates)의
p’4 = (x`(p)z, y`(p)z, z`(p)z, (p)z) 점으로 변환
————————————————————————————————————————————
x`(p)z의 동차좌표계공식에 2n/right-left * ((p)x/(p)z) - (right+left/right-left) 원근투영공식 x’ 대입
x`p(z) = (2n/right-left)(p)x - (right+left/right-left)p(z) - 동차좌표계 x`공식
y`(p)z의 동차좌표계공식에 2n/top-bottom * ((p)y/(p)z) - (top+bottom/top-bottom) 원근투영공식 y’ 대입
y`p(z)= (2n/top-bottom)(p)y - (top+bottom/top-bottom)(p)z- 동차좌표계의 y`공식
z`(p)z의 공식에 (fn/n-f)(1/(p)z) + f/f-n = z` 공식 대입
z`p(z) = (-fn/f-n) + (f/f-n)(p)z - 동차 좌표계의 z`공식
위 공식 3개를 행렬로 대입시키면, 최종 원근 투영변환의 행렬 공식이 도출된다.
————————————————————————————————————————————
2n / right - left 0 0 0
0 2n / top -bottomtom 0 0
-(right+left / right-left) -(top+bottom/top-bottom) far / far - near 1
0 0 -(far * near) / far - near 0
————————————————————————————————————————————
right - left = width, top - bottom = height로, left = -right, bottom = -top 으로 보고
————————————————————————————————————————————
2n / width 0 0 0
0 2n / height 0 0
0 0 far / far - near 1
0 0 -(far * near) / far - near 0
————————————————————————————————————————————
1. 복소수와 쿼터니온 그리고 회전 (0) | 2012.11.02 |
---|---|
동차좌표 (0) | 2012.11.02 |
회전행렬 (0) | 2012.11.02 |
회전 변환 (0) | 2012.11.02 |
일차 독립, 일차 종속 (0) | 2012.11.02 |
회전변환에 올렸던 내용중...
x ' = cos(a+b) = cos(a)cos(b) - sin(a)sin(b) = xcos(b) - ysin(b) (왜냐하면 x=cos(a), y=sin(a)이므로.)
인데 여기서
이것을 행렬로 바꿔준다면 (다이렉트 기준)
| cos | sin |
(x,y) | ------- | ---------|
| -sin | cos |
이렇게 들어갈 수 있다
즉 첫번째 열에 대한 연산이 x' 이 되고 두번째 열에 대한 행렬 연산이 y' 이 된다
동차좌표 (0) | 2012.11.02 |
---|---|
원근투형 TM (0) | 2012.11.02 |
회전 변환 (0) | 2012.11.02 |
일차 독립, 일차 종속 (0) | 2012.11.02 |
3D 벡터 기초 연산 (0) | 2012.11.02 |
[ cos(a+b) + i sin(a+b) ] =[ cos(a) +i sin(a) ] [ cos(b) +i sin(b) ]
는 삼각함수의 합성공식으로 증명할수 있습니다.
임의점은 (x,y)를 (x+iy)= r [cos(b) + i sin(b) ]와 같이 복소수로 표현할수 있는데
a만큼 회전하면 r [cos(a+b) + i sin(a+b) ]가 되겠죠
위식을 이용하면
r [cos(a+b) + i sin(a+b) ] = r [ cos(a) + i sin(a) ] [ cos(b) +i sin(b) ]
= [ cos(a) + i sin(a) ](x+iy) = xcos(a)-ysin(a) + i [ xsin(a)+ycos(b) ] 가 됩니다.
회전후 x좌표는 xcos(a)-ysin(a)
회전후 y좌표는 xsin(a)+ycos(b)
..복소수로 표현하지 않고, 단순히 (x',y')로 나타내도 된다. 또한 반지름을 일반적인 'r'이 아닌 '1'로 하면 더 간단하다.
(x ',y ') = Rotation(x,y) :
x ' = cos(a+b) = cos(a)cos(b) - sin(a)sin(b) = xcos(b) - ysin(b) (왜냐하면 x=cos(a), y=sin(a)이므로.)
같은 방식으로 y ' 도 나타낼 수 있다.
출처 : http://blog.naver.com/kjj0124?Redirect=Log&logNo=100048801821
원근투형 TM (0) | 2012.11.02 |
---|---|
회전행렬 (0) | 2012.11.02 |
일차 독립, 일차 종속 (0) | 2012.11.02 |
3D 벡터 기초 연산 (0) | 2012.11.02 |
왼손 좌표계 vs 오른손 좌표계 의 외적 (0) | 2012.11.02 |
회전행렬 (0) | 2012.11.02 |
---|---|
회전 변환 (0) | 2012.11.02 |
3D 벡터 기초 연산 (0) | 2012.11.02 |
왼손 좌표계 vs 오른손 좌표계 의 외적 (0) | 2012.11.02 |
directx 사원수 관련 함수 d3dx9math.h (0) | 2012.11.02 |
http://cafe.naver.com/ckgirl/90
[게임프로그래머를 위한 기초 수학과 물리]제4장 벡터연산 [완성] |
1. 벡터 vs 스칼라
우리가 흔히 하는 셈에서 다루는 값은 스칼라입니다. 그냥 스칼라는 그런수죠. 우리가 초,중,고 다니면서 수학시간에 열심히 계산할때 사용한 대부분의 수. 즉 방향이 없는 크기들입니다..
벡터는 방향과 크기를 모두 함께 가지고 있는 값입니다. 내가 가고자 하는 목적지가 지금 위치로부터 100미터 떨어져 있다와 북쪽으로 100미터 떨어져있다가 스칼라와 백터의 차이점입니다.
거리나 속력은 스칼라값을 말하는 단위입니다. 변위나 속도는 벡터값을 말하는 단위이고요.
변위는 거리라는 스칼라값을 벡터로 표현한것이고 속도는 속력이라는 스칼라값을 벡터로 표현한 것입니다.
시속 100km 는 속력이고 동쪽으로 시속 100km 라고 하면 속도가 되는 것이죠.
벡터의 값에는 방향이 있는데 이것을 다루는 방법을 이야기 하자면 1차원에서는 방향이 2개 입니다. 음수나 양수로 방향을 표시할 수 있습니다. 예를 들어서 오른쪽에서 왼쪽으로 스크롤되는 횡스크롤 게임(D&D같은)이 있다고 할때 캐릭터가 오른쪽으로 가면 양수이고 왼쪽으로 가면 음수가 되는 것입니다.
마찬가지로 위에서 아래로 종스크롤되는 게임(1945 striker 같은)에서는 위로가면 양수 아래로 가면 음수가 됩니다.
실제 수를 넣어서 문제를 보자면 위아래로만 움직일 수 있는 퐁 게임에서 반사판의 중심이 (20,400)에서 (20,50)까지 움직였다면 변위는 얼마일까요?
위,아래로만 움직일 수 있기 때문에 x좌표의 값은 변화없이 20으로 고정이 됩니다. 그렇다면 y 값을 살펴볼까요. y의 값이 400 에서 50으로 달라졌네요. 따라서 변위는 -350 입니다.
변위를 다룰때는 방향을 고려하는 것이 매우 중요합니다. 스칼라 양인 거리와 벡터량인 변위는 매우 큰 차이가 있기 때문입니다. 변위를 계산할때 고려해야 할 것은 시작점과 끝점의 위치뿐이고 그 사이에 일어나는 일은 그다지 중요하지 않습니다.
풋볼경기를 예로 들어보면 다음과 같습니다.
극좌표와 같이 길이와 방향으로 벡터를 기술하는 대신, 벡터를 수평과 수직 변위로 나타낼 수도 있으며 이것은 데카르트 좌표계의 방식과 매우 유사합니다. 데카르트 좌표를 구성하는 두가지 요소는 수평과 수직성분이며 이렇게 표현한 형식을 i j 형식이라고 합니다.
====================================================================
| 데카르트 좌표 (벡터 성분)
| 벡터의 x 축 방향의 단위벡터를 i^ , y 축 방향의 단위벡터를 j^ 라고하면,
| 벡터 B = b1 i^ + b2 j^
======================================================================
** 단위벡터를 나타내는 문자가 원래는 i 위에 ^ 이 모자처럼 씌어진 그림입니다만 키보드에서 적절한 문자를 찾을 수 없어서 뒤에다가 썻으니 이점을 숙지하여 다른 책이나 자료를 볼때 혼동하지 마시길바랍니다. 아래 그림을 참고하시기 바랍니다.
[데카르트 좌표로 표현된 벡터 B]
컴퓨터의 화면은 격자 형태로 설정되습니다. 그렇기 때문에 코딩을 할때 데카르트 좌표를 사용합니다.
혹 극좌표를 사용하려고 한다고 해도 결국은 데카르트 좌표로 변환하여 사용해야 합니다. 극좌표에서 데카르트좌표로 변환하는 방법은 아래 그림과 같습니다.
두 벡터의 시작점과 끝점을 일치하도록 만들었다면, 움직이지 않은 벡터의 시작점과 움직인 벡터의 끝점을 연결하여 새로운 벡터를 그리면 이 새 벡터가 바로 두 벡터의 합입니다.
아래 설명의 출처 Direct 3D 기초 수학 #1 벡터 |작성자 야누스
Direct 3D 기초 수학 #1 벡터
[출처] Direct 3D 기초 수학 #1 벡터 |작성자 야누스
* 벡터 : 크기와 방향을 가지는 물리량
* 포인트 (점,Vertex): 한점또는 위치만을 가진다
* 스칼라(Scalar : 크기 . 길이등 한가지만을 표현할때 사용
* 단위벡터 : 크기가 1인 벡터 표기는 ||V|| 와 같이한다.
- 단위벡터는 광선의 방향, 편명의 법선방향 , 카메라의 방향등 방향을 나타낼때
사용된다 크기가 1이 아닌 벡터를 단위벡터로 만들어주는 과정을 정규화(Normalize)
라고 하며 벡터를 자신의 크기로 나눠줌으로 얻을수 있다
벡터의크기 : 벡터 U의 크기 ||U|| = sqrt(Ux^,Uy^,Uz^);
단위벡터 : U / ||U|| ;
*내적의 정의 : 내적의 곱의 결과값은 스칼라이므로 스칼라곱(scalar product)이라고 한다 .
두개 의 벡터 A , B 의 내적을 구해보자
∮(각 세타이다)
내적구하는공식
A * B = = AxBx + AyBy + AzBz ;
두벡터간의 사이갓을 구하는공식
A * B = || A || * || B || cos∮ = cos∮ = A * B / || A || || B || =
∮ = 1/cos(A * B / || A || || B ||) ;
두벡터의 사잇각의 관계
A * B = 0 이면 A ⊥ B 이다
A * B > 0 이면 사잇각은 90보다 작다
A * B < 0 이면 사잇각은 90 보다 크다
A 투영 B ( B에 평행한 분해 벡터) = ((내적) / (B의 Length) * (B의 Length)) * B벡터 ;
A 수직 B ( B에 수직한 분해 벡터 ) = A - ((내적) / (B의 Length) * (B의 Length)) * B벡터 ;
외적의 정의 : 원전에 접선된 두개의 접선벡터에 수직한 벡터를 결과로 갖는 값 .
법선벡터를 구할때 사용한다 .
한평면의 세점 A , B , C 에 대하여 A(Ax,Ay,Az),B(Bx,By,Bz) ,C(Cx,Cy,Cz)에 대하여
점 A를 기준으로 점 B 와 C 에 대한 벡터를 각각 U , V 라 할때 수식을 통하여 벡터를 구할수잇다
U = B(Bx,By,Bz) - A(Ax,Ay,Az) = [Bx -Ax , By - Ay , Bz - Az];
V = C(Cx,Cy,Cz) - A(Ax,Ay,Az) = [Cx - Ax , Cy - Ay, Cz - Az];
두벡터간의 법선 벡터를 구하면 외적을 구할수 있다
외적구하는 공식
x = Uy * Vz - Uz * Vy;
y = Uz * Vx - Ux * Vz;
Z = Ux * Vy - Uy * Vx; 새로운 벡터 E(Ex,Ey,Ez) 두벡터 U,V에 직각인 법선벡터이다 .
D3DX에서 사용되는 함수 이다 , D3DXVECTOR3 클래스는 라이브러리에서 지원되는 클래스이다
크기 : float D3DXVec3Length(D3DXVECTOR* pV);
설명 : 반환값은 스칼라이고 전달인자는 벡터의 &(포인터)이다.
ex) float magnitude = D3DXVec3Length(&V);
단위벡터(Normalize : 정규화) :
D3DXVECTOR3* D3DVec3Normalize(D3DXVECTOR3* pOut,D3DXVECTOR3* pV);
설명 : 반환값은 벡터의 포인터 형태이고 전달인자는 정규화가 끝난 값을 보관할 벡터의 포인터형과 정규화할 벡터의 포인터형태이다
ex) D3DXVECTOR3 U = D3DXVec3Normalize(&pOut,&pV);
내적(Dot product) float D3DXVec3Dot(D3DVECTOR3* pV1 , D3DVECTOR3* pV2);
설명 : 반환값은 스칼라이며 인자는 왼쪽의 피연산벡터의 포인터 , 오른쪽 피연산벡터의 포인터이다
ex)float result = D3DXVec3Dot(&pV1, &pV2);
외적(Corss product) :
D3DVECTOR3* D3DXVec3Cross(D3DXVECTOR3* pOut,D3DVECTOR3* pV1,D3DVECTOR3* pV2);
설명 : 반환값은 연산이 완료된 벡터의 포인터이고 인자는 리턴하기전 결과를 저장할 벡터의 포인터, 왼쪽 피연산벡터의 포인터 , 오른쪽 피연산벡터의 포인터);
ex D3DVECTOR3 Cross = D3DXVec3Cross(&pOut,&pv1 , &pv2);
기억하면 좋을 사항
외적은 교환법칙이 성립되지않는다 , U * V = - (V * B) 이다
벡터의 곱이 스칼라 이면 내적이고 벡터의 곱이 벡터이면 외적이다
회전 변환 (0) | 2012.11.02 |
---|---|
일차 독립, 일차 종속 (0) | 2012.11.02 |
왼손 좌표계 vs 오른손 좌표계 의 외적 (0) | 2012.11.02 |
directx 사원수 관련 함수 d3dx9math.h (0) | 2012.11.02 |
GPU 에서 노멀맵 굽기 (0) | 2012.11.02 |
3DMP engines3D그래픽스 물리 수학, 프로그래밍 GPU Shader 게임엔진 알고리즘 디자인패턴 matlab etc..
연산은 동일한데 포인트는, 기본 좌표계에따라 외적의 방향이 달라진다는 것
이하 첨부내용
http://blog.naver.com/9992028/120020809381
|
일차 독립, 일차 종속 (0) | 2012.11.02 |
---|---|
3D 벡터 기초 연산 (0) | 2012.11.02 |
directx 사원수 관련 함수 d3dx9math.h (0) | 2012.11.02 |
GPU 에서 노멀맵 굽기 (0) | 2012.11.02 |
표면 스케터링으로 실시간 근사 (0) | 2012.11.02 |
d3dx9math.h 의 내용
//--------------------------
// Quaternion
//--------------------------
// inline
FLOAT D3DXQuaternionLength
( CONST D3DXQUATERNION *pQ );
// Length squared, or "norm"
FLOAT D3DXQuaternionLengthSq
( CONST D3DXQUATERNION *pQ );
FLOAT D3DXQuaternionDot
( CONST D3DXQUATERNION *pQ1, CONST D3DXQUATERNION *pQ2 );
// (0, 0, 0, 1)
D3DXQUATERNION* D3DXQuaternionIdentity
( D3DXQUATERNION *pOut );
BOOL D3DXQuaternionIsIdentity
( CONST D3DXQUATERNION *pQ );
// (-x, -y, -z, w)
D3DXQUATERNION* D3DXQuaternionConjugate
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ );
// non-inline
#ifdef __cplusplus
extern "C" {
#endif
// Compute a quaternin's axis and angle of rotation. Expects unit quaternions.
void WINAPI D3DXQuaternionToAxisAngle
( CONST D3DXQUATERNION *pQ, D3DXVECTOR3 *pAxis, FLOAT *pAngle );
// Build a quaternion from a rotation matrix.
D3DXQUATERNION* WINAPI D3DXQuaternionRotationMatrix
( D3DXQUATERNION *pOut, CONST D3DXMATRIX *pM);
// Rotation about arbitrary axis.
D3DXQUATERNION* WINAPI D3DXQuaternionRotationAxis
( D3DXQUATERNION *pOut, CONST D3DXVECTOR3 *pV, FLOAT Angle );
// Yaw around the Y axis, a pitch around the X axis,
// and a roll around the Z axis.
D3DXQUATERNION* WINAPI D3DXQuaternionRotationYawPitchRoll
( D3DXQUATERNION *pOut, FLOAT Yaw, FLOAT Pitch, FLOAT Roll );
// Quaternion multiplication. The result represents the rotation Q2
// followed by the rotation Q1. (Out = Q2 * Q1)
D3DXQUATERNION* WINAPI D3DXQuaternionMultiply
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ1,
CONST D3DXQUATERNION *pQ2 );
D3DXQUATERNION* WINAPI D3DXQuaternionNormalize
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ );
// Conjugate and re-norm
D3DXQUATERNION* WINAPI D3DXQuaternionInverse
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ );
// Expects unit quaternions.
// if q = (cos(theta), sin(theta) * v); ln(q) = (0, theta * v)
D3DXQUATERNION* WINAPI D3DXQuaternionLn
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ );
// Expects pure quaternions. (w == 0) w is ignored in calculation.
// if q = (0, theta * v); exp(q) = (cos(theta), sin(theta) * v)
D3DXQUATERNION* WINAPI D3DXQuaternionExp
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ );
// Spherical linear interpolation between Q1 (t == 0) and Q2 (t == 1).
// Expects unit quaternions.
D3DXQUATERNION* WINAPI D3DXQuaternionSlerp
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ1,
CONST D3DXQUATERNION *pQ2, FLOAT t );
// Spherical quadrangle interpolation.
// Slerp(Slerp(Q1, C, t), Slerp(A, B, t), 2t(1-t))
D3DXQUATERNION* WINAPI D3DXQuaternionSquad
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ1,
CONST D3DXQUATERNION *pA, CONST D3DXQUATERNION *pB,
CONST D3DXQUATERNION *pC, FLOAT t );
// Setup control points for spherical quadrangle interpolation
// from Q1 to Q2. The control points are chosen in such a way
// to ensure the continuity of tangents with adjacent segments.
void WINAPI D3DXQuaternionSquadSetup
( D3DXQUATERNION *pAOut, D3DXQUATERNION *pBOut, D3DXQUATERNION *pCOut,
CONST D3DXQUATERNION *pQ0, CONST D3DXQUATERNION *pQ1,
CONST D3DXQUATERNION *pQ2, CONST D3DXQUATERNION *pQ3 );
// Barycentric interpolation.
// Slerp(Slerp(Q1, Q2, f+g), Slerp(Q1, Q3, f+g), g/(f+g))
D3DXQUATERNION* WINAPI D3DXQuaternionBaryCentric
( D3DXQUATERNION *pOut, CONST D3DXQUATERNION *pQ1,
CONST D3DXQUATERNION *pQ2, CONST D3DXQUATERNION *pQ3,
FLOAT f, FLOAT g );
3D 벡터 기초 연산 (0) | 2012.11.02 |
---|---|
왼손 좌표계 vs 오른손 좌표계 의 외적 (0) | 2012.11.02 |
GPU 에서 노멀맵 굽기 (0) | 2012.11.02 |
표면 스케터링으로 실시간 근사 (0) | 2012.11.02 |
D3DXMatrixTransformation2D (0) | 2012.11.02 |
Diogo Teixeira
Move Interactive
Ever since normal mapping (Heidrich and Seidel 1999) became a standard technique in modern real-time rendering, it has raised productivity issues for artists as they strive to meet the demands of higher visual standards within their usual time constraints. The use of adequate tools, however, helps reduce production time—as in the case of normal-map baking, where minutes can add up to hours and inflate production costs. The limiting factor, though, has been the computing power required to make baking time a trivial issue.
The modern GPU is an extremely valuable resource for scientific computing and visualization. It has yet, however, to be fully tapped as a viable computing alternative for artistic asset production. Sometimes production costs can be considerably reduced without investing in additional hardware resources. The goal in this chapter is to prove that any midrange Shader Model 3.0 graphics card released in the last two years can be used to generate normal maps faster than most CPU-based tools. This is possible because the type of computation required is highly parallel and independent, therefore ideal for a stream processor.
In this chapter we start by analyzing how the traditional ray-cast-based normal-map projection technique can be successfully implemented on a graphics processor. We also address common problems such as memory limitations and antialiasing.
The traditional algorithm for baking a normal map consists of projecting points at the surface of a low-polygon mesh (theworking model) in the direction of a high-polygon mesh (the reference model). For simplification purposes, we will assume that both meshes are triangulated.
In this implementation, the working model needs a texture coordinate set that we will use to rasterize the working model into texture space. The reference model, however, does not need texture coordinates. This is probably one of the best advantages of this implementation, because building decent texture coordinates for really dense polygon meshes can be troublesome.
Additionally, the working model also includes information about the position of the boundary cage at each vertex.
The projection is done first by rasterizing the working model's triangles into texture space, as shown in Figure 22-1, and using the hardware interpolators to interpolate the surface position, tangent basis, and texture coordinate at each pixel from the triangles' corners.
Figure 22-1 A Sample Model
The next step is to trace a ray from the interpolated boundary cage position in the direction of the reference model. This may result in more than one hit along the direction of the ray, and from each hit we will derive a candidate normal. We then select the most appropriate normal, which in our case will be the closest to the boundary cage.
The boundary cage (or projection cage) usually consists of a mesh extruded from the working model. The artist may visually modify the cage to provide finer control over the way the rays are cast and to avoid complications in certain regions of the mesh.
The cage surface will serve as a starting point for the rays being cast onto the reference model. The vector from each vertex in the cage to the matching vertex on the working model is also interpolated during rasterization and used as the ray direction.
Usually the cage is built so that it encloses both the reference model and the working model. As you can see from the sample inFigure 22-2, if a ray is cast as previously described, we can assume that the hit closest to the cage can be used to extract the normal for the current pixel.
Figure 22-2 A Boundary Cage
This implementation relies heavily on the ray tracing of high-polygon meshes, which can be extremely computational intensive without the use of proper acceleration structures. This is obviously also an issue when dealing with ray-tracing algorithms on the GPU (Purcell et al. 2002, Christen 2005). In this case, the choice of an acceleration structure is less trivial because we are dealing with an architecture designed for highly parallel vertex and fragment shading (Lindholm et al. 2001).
We need a structure that maps well to GPU memory using texture resources. We also need to take advantage of the fact that the working and reference meshes are mostly spatially coherent (in other words, they roughly share the same space).
The uniform grid, as shown in Figure 22-3, is a spatial subdivision scheme that partitions the mesh into constant-size voxels or cells, as described in Fujimoto and Iwata 1985.
Figure 22-3 A Perspective View of a Uniform Grid
To date, most publications on GPU ray tracing use a uniform grid for spatial subdivision and 3D digital differential analyzer (3D-DDA) for grid traversal. The structure maps well to GPU memory and the traversal algorithm is simple enough to be implemented in a pixel shader.
For general-purpose ray-tracing computations, the uniform grid may not be the best option (Havran 2001). In our specific case, however, it is a very good candidate. As you can see in Figure 22-4, where the grid overlays both models, the working model's surface is always very close to that of the reference model. This coherence can be exploited efficiently using a 3D-DDA traversal algorithm.
Figure 22-4 Both Models and the Grid Overlaid
First introduced by Amanatides and Woo 1987, the 3D-DDA algorithm allows a fast voxel traversal between two voxels in a uniform grid.
The 3D-DDA algorithm requires only a small number of steps to correctly guide the ray as it travels between neighboring cells. The cost of traversal is linear to the number of voxels visited. In most cases, however, the path is very short, which reduces the average traversal cost. Also note that the voxels, and therefore the geometry, are visited in the correct order, meaning that a valid hit will occlude any hits in the remaining cells. Figure 22-5 illustrates this approach.
Figure 22-5 A Ray Traversing Grid Cells Using 3D-DDA
This algorithm, along with the uniform grid scheme, is a key component in exploring the spatial coherency between the meshes involved in the projection.
Mapping the uniform grid and reference geometry data to hardware has been presented almost the same way in most publications about GPU ray tracing. The data structure used in this chapter is mostly based on previous publications on GPU ray tracing, with the exception of using indexed triangle lists to save video memory. See Figure 22-6 for a representation of the data structure.
Figure 22-6 A Visual Representation of Data Mapped to the GPU
The uniform grid is stored as a 3D texture with the same size. Each voxel contains a pointer to the triangle list of the respective grid cell. Addressing the uniform grid consists simply of using normalized cubic coordinates when fetching data from the 3D texture. We will be referring to cubic coordinates as values between zero and the size of the grid. Helper functions to convert between grid coordinates are shown in Listing 22-1.
The triangle lists for each cell are stored in a separate 2D texture. Each entry on the triangle list contains a triplet of pointers to the respective triangle vertices in the geometry textures. The list is terminated by a null pointer. If the first pointer is null, it means the cell is empty. Addressing the 2D textures as a linear array requires a simple conversion, as you can see in Listing 22-2.
The reference mesh is stored as a set of 2D textures. We keep the vertex positions and the normals separated because they won't need full 32-bit precision.
For simplification purposes, we use 32-bit float indices, which provide us with an effective indexing range of 24 bits. This is probably enough because we are using only one texture for each data type and dealing with graphics cards that have a maximum texture size of 4,096.
To build a robust implementation and support reference meshes with millions of triangles, we could go beyond 24-bit indexing by using a packed 32-bit (RGBA) integer format. This way we could store the mesh across a maximum of 256 textures, using one of the packed elements to index the texture and the remaining element to index the data within the texture. However, directly addressing multiple textures in DirectX 9.0 class cards might involve emulating texture arrays with a 3D texture. This type of addressing could also be used on pointers stored in the grid texture.
The base hardware specification used to test the solution presented in this chapter consisted of a Shader Model 3.0 medium-range ATI graphics card with 256 MB. This specific card is limited to power-of-two volumes, so the highest resolution volume we can safely allocate on this card is 256x256x256. However, when working with dense reference meshes with millions of polygons, we need to use a higher-resolution uniform grid to reduce the cost of visiting a cell containing geometry. Some NVIDIA cards already support non-power-of-two volumes.
Table 22-1 provides the memory usage statistics for a sample reference mesh with 700,000 triangles. Note that all textures used are power-of-two.
Description | Dimensions | Format | Size |
Grid Texture | 256x256x256 | R32F | 64 MB |
Triangle List Texture | 4096x2048 | R32F | 32 MB |
Vertex Texture | 1024x1024 | A32B32G32R32F | 8 MB |
Normal Texture | 1024x1024 | A16B16G16R16F | 4 MB |
Total Size: | 108 MB |
Although the vertex and normal textures consume only a small portion of the memory footprint required, they can be packed tighter, trading off accuracy for memory. The vertex texture could be reduced by half by using a 16-bit floating-point texture format (for example, A16B16G16R16F), which would be precise enough for some models. The normals could also be stored in a low-precision texture using a 32-bit packed format such as A8R8G8B8, which would also halve the memory costs.
In this chapter we analyze only a basic implementation of the typical projection using the pixel shader. This version requires only a single rendering pass of the working model. It has, however, a few limitations related to static flow control on Shader Model 3.0 hardware, where the maximum number of iterations for each loop must not exceed 256.
Later we discuss possible workarounds for this limitation, including an implementation that involves multipass and tiled rendering. And at the end of this section, we cover antialiasing.
The first step in implementing the projection is to load both meshes and generate a uniform grid based on the high-polygon reference model. The grid and geometry data are then mapped to textures in video memory so they can be used by the pixel shader.
The working model, however, should be mapped to hardware vertex buffers and include a tangent basis, which will be used to transform the world-space normal (extracted from the reference model) into tangent space. A cage position and a texture coordinate set for each vertex are also required.
Texture samplers—which include the grid volume, the triangle list, and the geometry textures—must be set before rendering. Sampling filters must be disabled because we are using integer indexing. Clamp addressing should also be set.
Listing 22-3 shows a few common data structures declared in the beginning of the shader. These define a grid cell, an axial-aligned bounding box, a ray, and a triangle. Also listed are a few utility functions to access the grid cells, and a few to access geometry texture data.
Please note that the functions RayAABBIntersect() and RayTriangleIntersect() are not included in the listing, but they can be found in the complete shader provided on this book's accompanying DVD.
In this version of the algorithm, we render the working model directly to the normal-map render target only once. Following is a description of the steps taken inside the pixel shader.
As shown in Listing 22-4, we start by using the interpolated vertex position and cage vector, both sent by the vertex shader, to set up the ray for the current pixel. We may need to adjust the origin of the ray if the cage falls outside the grid, as shown inFigure 22-7. We do this by intersecting the ray with the uniform grid's box to compute the first position along the ray that falls inside the grid. We use the adjusted origin of the ray to find the cell where we will begin the traversal.
Figure 22-7 The Cage May Fall Outside the Grid at Some Points
The next step is to compute the vectors step, tmax, and delta, as described in Amanatides and Woo 1987, where step indicates whether the current position should be incremented or decremented as the ray crosses voxel boundaries. The vector tmax stores the value of t at which the ray crosses the voxel boundary in each axis. Delta indicates how far along the ray we must move (in units of t) along each direction to match the dimensions of the voxel. Listing 22-5 shows how to initialize step, tmax, and delta.
We are now ready to begin traversing the grid. As you can see, the traversal code requires only a few instructions, which can be further optimized using vectorized static branching. The main loop is used to traverse the grid to the next cell, while the nested secondary loop is used to test the ray against all triangles in the current cell. See Listings 22-6 and 22-7.
This function checks all triangles in the cell and returns the normal closest to the ray origin (in the cage).
Traversing the grid searching for a valid normal.
To complete the pixel shader, we just need to convert the normal into the working model's tangent space and scale it to the 0..255 range to be stored in a bitmap, as shown in Listing 22-8.
As we discussed earlier, these loops in ps_3_0 are limited to 256 iterations. A possible workaround is to use two nested loops instead of a single loop, as shown in Listing 22-9, to theoretically achieve a total of 65,536 iterations for each nested pair. The DirectX 9.0 documentation does not address this specific case, though it does mention that the maximum nested loop depth is 4 and the maximum number of instructions executed by a pixel shader must be at least 65,536. This limit can be reached quite easily with a sufficiently complex reference model and a denser grid. You might have to revert to a multipass approach (described in Section 22.4.3) if the host GPU has such instruction limits.
To avoid having four nested loops inside the pixel shader, we could move some work to the CPU and perform the projection in multiple passes. To achieve this, we would need to split the pixel shader.
The single and most obvious limitation of this implementation is the memory requirement to store all the temporary variables in texture memory; however, tiled rendering solves this problem.
As described in Purcell et al. 2002, the memory requirement limitation can be overcome by splitting the rendering into pieces or tiles. One tile would be rendered at a time and copied to a texture in system memory, allowing the render textures to be reused. The size of the tiles could be defined according to the available resources.
The technique presented in this chapter can be extended to support antialiasing in at least two ways:
The reference model for our test sample has around 560,000 triangles. The working model, on the other hand, has only 868 triangles. Figures 22-8 and 22-9 show the models.
Figure 22-8 The Reference Model
Figure 22-9 The Working Model
Preparing the reference model for projection takes only 5.2 seconds: 3.6 seconds to load the file, 1.0 second to generate the uniform grid, and 0.6 seconds to feed the data to the GPU. Please keep in mind that the CPU part of the implementation is not yet fully optimized and is using only a single core.
These timings were recorded on a notebook with a T2400 Core Duo processor, 1 GB DDR2 memory at 667 MHz, and a 5400 rpm hard drive. The graphics processor is an ATI X1600 with 256 MB of dedicated video memory.
The charts in Figure 22-10 compare the GPU implementation described in this chapter with two popular CPU-based implementations, NVIDIA Melody and xNormal. The boundary cage used is similar in all tests and the settings are the simplest possible, with no antialiasing, padding, or dilation.
Figure 22-10 Performance Comparison Charts
The resulting normal map shows minor precision issues that can be solved by improving the numerical robustness of the algorithm in the pixel shader. See Figure 22-11.
Figure 22-11 Resultant Normal Map and Its Application
The normal map was rendered against a black background for illustration purposes only. Creating the map against a midblue background (rather than black) will ameliorate mipmapping issues.
The reference mesh could be stored using triangle strips instead of lists: this would save texture memory by storing fewer triangle indices. It would also improve the performance in the pixel processor by reducing texture fetches.
The preprocessing part of this implementation, which runs on the CPU, could be optimized by using Streaming SIMD Extensions and OpenMP.
With minor modifications, this technique could be used to generate displacement maps, which can also be used for parallax and relief mapping. With a more complex shader, we could also generate local ambient occlusion or cavity maps.
One advantage of this technique is that it's fast enough to permit (and encourage) individually rendering all the mip levels explicitly, improving shading quality in mipmapped areas.
Because of the nature of the computations required for this technique, it scales very well with multithread-based architectures, which the latest generation of GPUs features.
In this chapter we have shown that the graphics processor can generate normal maps very efficiently, which allows fast, high-resolution previews.
Amanatides, John, and Andrew Woo. 1987. "A Fast Voxel Traversal Algorithm for Ray-tracing." In Proceedings of Eurographics '87, pp. 3–10. Available online at http://www.cse.yorku.ca/~amana/research/.
Christen, Martin. 2005. "Ray Tracing on GPU." University of Applied Sciences Basel, diploma thesis. Available online athttp://www.clockworkcoders.com/oglsl/rt/.
Cohen, Jonathan, Marc Olano, and Dinesh Manocha. 1998. "Appearance-Preserving Simplification." In Proceedings of the 25th Annual Conference on Computer Graphics and Interactive Techniques, pp. 115–122. Available online athttp://www.cs.unc.edu/~geom/APS/.
Fujimoto, A., and K. Iwata. 1985. "Accelerated Ray Tracing." In Computer Graphics: Visual Technology and Art (Proceedings of Computer Graphics, Tokyo '85), pp. 41–65.
Havran, Vlastimil. 2001. "Heuristic Ray Shooting Algorithms." Czech Technical University, Ph.D. dissertation. Available online athttp://www.cgg.cvut.cz/~havran/phdthesis.html.
Heidrich, Wolfgang, and Hans-Peter Seidel. 1999. "Realistic, Hardware-accelerated Shading and Lighting." In Proceedings of SIGGRAPH 99, pp. 171–178. Available online at http://www.cs.ubc.ca/~heidrich/Papers/Siggraph.99.pdf.
Krishnamurthy, Venkat, and Marc Levoy. 1996. "Fitting Smooth Surfaces to Dense Polygon Meshes." In Proceedings of the 23rd Annual Conference on Computer Graphics and Interactive Techniques, pp. 313–324. Available online athttp://graphics.stanford.edu/papers/surfacefitting/.
Lindholm, Erik, Mark Kilgard, and Henry Moreton. 2001. "A User Programmable Vertex Engine." Presentation at SIGGRAPH 2001. Available online at http://developer.nvidia.com/object/SIGGRAPH_2001.html.
Purcell, Timothy J., Ian Buck, William R. Mark, and Pat Hanrahan. 2002. "Ray Tracing on Programmable Graphics Hardware." InACM Transactions on Graphics (Proceedings of SIGGRAPH 2002) 21(3), pp. 703–712. Available online athttp://graphics.stanford.edu/papers/rtongfx/.
왼손 좌표계 vs 오른손 좌표계 의 외적 (0) | 2012.11.02 |
---|---|
directx 사원수 관련 함수 d3dx9math.h (0) | 2012.11.02 |
표면 스케터링으로 실시간 근사 (0) | 2012.11.02 |
D3DXMatrixTransformation2D (0) | 2012.11.02 |
로컬좌표와 월드 좌표 (0) | 2012.11.02 |
Simon Green
NVIDIA
Most shading models used in real-time graphics today consider the interaction of light only at the surface of an object. In the real world, however, many objects are slightly translucent: light enters their surface, is scattered around inside the material, and then exits the surface, potentially at a different point from where it entered.
Much research has been devoted to producing efficient and accurate models of subsurface light transport. Although completely physically accurate simulations of subsurface scattering are out of the reach of current graphics hardware, it is possible to approximate much of the visual appearance of this effect in real time. This chapter describes several methods of approximating the look of translucent materials, such as skin and marble, using programmable graphics hardware.
When trying to reproduce any visual effect, it is often useful to examine images of the effect and try to break down the visual appearance into its constituent parts.
Looking at photographs and rendered images of translucent objects, we notice several things. First, subsurface scattering tends to soften the overall effect of lighting. Light from one area tends to bleed into neighboring areas on the surface, and small surface details become less visible. The farther the light penetrates into the object, the more it is attenuated and diffused. With skin, scattering also tends to cause a slight color shift toward red where the surface transitions from being lit to being in shadow. This is caused by light entering the surface on the lit side, being scattered and absorbed by the blood and tissue beneath the skin, and then exiting on the shadowed side. The effect of scattering is most obvious where the skin is thin, such as around the nostrils and ears.
One simple trick that approximates scattering is wrap lighting. Normally, diffuse (Lambert) lighting contributes zero light when the surface normal is perpendicular to the light direction. Wrap lighting modifies the diffuse function so that the lighting wraps around the object beyond the point where it would normally become dark. This reduces the contrast of the diffuse lighting, which decreases the amount of ambient and fill lighting that is required. Wrap lighting is a crude approximation to the Oren-Nayar lighting model, which attempts to more accurately simulate rough matte surfaces (Nayar and Oren 1995).
The code shown here and the graph in Figure 16-1 illustrate how to change the diffuse lighting function to include the wrap effect. The value wrap is a floating-point number between 0 and 1 that controls how far the lighting will wrap around the object.
Figure 16-1 Graph of the Wrap Lighting Function
float diffuse = max(0, dot(L, N)); float wrap_diffuse = max(0, (dot(L, N) + wrap) / (1 + wrap));
To compute this efficiently in a fragment program, the function can be encoded in a texture, which is indexed by the dot product between the light vector and the normal. This texture can also be created to include a slight color shift toward red when the lighting approaches zero. This is a cheap way to simulate scattering for skin shaders. The same texture can also include the power function for specular lighting in the alpha channel. The FX code in Listing 16-1 demonstrates how to use this technique. See Figure 16-2 for examples.
Figure 16-2 Applying Wrap Lighting to Spheres
// Generate 2D lookup table for skin shading float4 GenerateSkinLUT(float2 P : POSITION) : COLOR { float wrap = 0.2; float scatterWidth = 0.3; float4 scatterColor = float4(0.15, 0.0, 0.0, 1.0); float shininess = 40.0; float NdotL = P.x * 2 - 1; // remap from [0, 1] to [-1, 1] float NdotH = P.y * 2 - 1; float NdotL_wrap = (NdotL + wrap) / (1 + wrap); // wrap lighting float diffuse = max(NdotL_wrap, 0.0); // add color tint at transition from light to dark float scatter = smoothstep(0.0, scatterWidth, NdotL_wrap) * smoothstep(scatterWidth * 2.0, scatterWidth, NdotL_wrap); float specular = pow(NdotH, shininess); if (NdotL_wrap <= 0) specular = 0; float4 C; C.rgb = diffuse + scatter * scatterColor; C.a = specular; return C; } // Shade skin using lookup table half3 ShadeSkin(sampler2D skinLUT, half3 N, half3 L, half3 H, half3 diffuseColor, half3 specularColor) : COLOR { half2 s; s.x = dot(N, L); s.y = dot(N, H); half4 light = tex2D(skinLUT, s * 0.5 + 0.5); return diffuseColor * light.rgb + specularColor * light.a; }
One of the most important factors in simulating very translucent materials is absorption. The farther through the material light travels, the more it is scattered and absorbed. To simulate this effect, we need a measure of the distance light has traveled through the material.
One method of estimating this distance is to use depth maps (Hery 2002). This technique is very similar to shadow mapping, and it is practical for real-time rendering. In the first pass, we render the scene from the point of view of the light, storing the distance from the light to a texture. This image is then projected back onto the scene using standard projective texture mapping. In the rendering pass, given a point to be shaded, we can look up into this texture to obtain the distance from the light at the point the ray entered the surface (di). By subtracting this value from the distance from the light to the point at which the ray exited the surface (do), we obtain an estimate of the distance the light has traveled through the object (s). See Figure 16-3.
Figure 16-3 Calculating the Distance Light Has Traveled Through an Object Using a Depth Map
The obvious problem with this technique is that it works only with convex objects: holes within the object are not accounted for correctly. In practice, this is not a big issue, but it may be possible to get around the problem using depth peeling, which removes layers of the object one by one (Everitt 2003).
You might be thinking that for static objects, it would be possible to paint or precalculate a map that represents the approximate thickness of the surface at each point. The advantage of using depth maps is they take into account the direction of the incoming light, and they also work for animating models (assuming that you regenerate the depth map each frame).
The programs in Listings 16-2 and 16-3 demonstrate how to render distance from the light to a texture. They assume the modelView and modelViewProj matrices have been set up by the application for the light view.
struct a2v { float4 pos : POSITION; float3 normal : NORMAL; }; struct v2f { float4 hpos : POSITION; float dist : TEXCOORD0; // distance from light }; v2f main(a2v IN, uniform float4x4 modelViewProj, uniform float4x4 modelView, uniform float grow) { v2f OUT; float4 P = IN.pos; P.xyz += IN.normal * grow; // scale vertex along normal OUT.hpos = mul(modelViewProj, P); OUT.dist = length(mul(modelView, IN.pos)); return OUT; }
float4 main(float dist : TEX0) : COLOR { return dist; // return distance }
The fragment program extract in Listing 16-4 shows how to look up in the light distance texture to calculate depth. For flexibility, this code does the projection in the fragment program, but if you are taking only a few samples, it will be more efficient to calculate these transformations in the vertex program.
// Given a point in object space, lookup into depth textures // returns depth float trace(float3 P, uniform float4x4 lightTexMatrix, // to light texture space uniform float4x4 lightMatrix, // to light space uniform sampler2D lightDepthTex, ) { // transform point into light texture space float4 texCoord = mul(lightTexMatrix, float4(P, 1.0)); // get distance from light at entry point float d_i = tex2Dproj(lightDepthTex, texCoord.xyw); // transform position to light space float4 Plight = mul(lightMatrix, float4(P, 1.0)); // distance of this pixel from light (exit) float d_o = length(Plight); // calculate depth float s = d_o - d_i; return s; }
Once we have a measure of the distance the light has traveled through the material, there are several ways we can use it. One simple way is to use it to index directly into an artist-created 1D texture that maps distance to color. The color should fall off exponentially with distance. By changing this color map, and combining the effect with other, more traditional lighting models, we can produce images of different materials, such as marble or jade.
float si = trace(IN.objCoord, lightTexMatrix, lightMatrix, lightDepthTex); return tex1D(scatterTex, si);
Alternatively, we can evaluate the exponential function directly:
return exp(-si * sigma_t) * lightColor;
The problem with this technique is that it does not simulate the way light is diffused as it passes through the object. When the light is behind the object, you will often clearly see features from the back side of the object showing through on the front. The solution to this is to take multiple samples at different points on the surface or to use a different diffusion approximation, as discussed in the next section.
On GeForce FX hardware, when reading from a depth texture, only the most significant eight bits of the depth value are available. This is not sufficient precision. Instead, we can either use floating-point textures or use the pack and unpack instructions from the NVIDIA fragment program extension to store a 32-bit float value in a regular eight-bit RGBA texture. Floating-point textures do not currently support filtering, so block artifacts will sometimes be visible where the projected texture is magnified. If necessary, bilinear filtering can be performed in the shader, at some performance cost.
Another problem with projected depth maps is that artifacts often appear around the edges of the projection. These are similar to the self-shadowing artifacts seen with shadow mapping. They result mainly from the limited resolution of the texture map, which causes pixels from the background to be projected onto the edges of the object. The sample code avoids this problem by slightly scaling the object along the vertex normal during the depth-map pass.
For more accurate simulations, we may also need to know the normal, and potentially the surface color, at the point at which the light entered the object. We can achieve this by rendering additional passes that render the extra information to textures. We can look up in these textures in a similar way to the depth texture. On systems that support multiple render targets, it may be possible to collapse the depth, normal, and other passes into a single pass that outputs multiple values. See Figure 16-4.
Figure 16-4 Using a Depth Map to Approximate Scattering
More sophisticated models attempt to accurately simulate the cumulative effects of scattering within the medium.
One model is the single scattering approximation, which assumes that light bounces only once within the material. By stepping along the refracted ray into the material, one can estimate how many photons would be scattered toward the camera. Phase functions are used to describe the distribution of directions in which light is scattered when it hits a particle. It is also important to take into account the Fresnel effect at the entry and exit points.
Another model, the diffusion approximation, simulates the effect of multiple scattering for highly scattering media, such as skin.
Unfortunately, these techniques are beyond the scope of this chapter.
Christophe Hery's chapter from the SIGGRAPH 2003 RenderMan course (Hery 2003) goes into the details of single and diffusion scattering for skin shaders.
As we noted earlier, one of the most obvious visual signs of subsurface scattering is a general blurring of the effects of lighting. In fact, 3D artists often emulate this phenomenon in screen space by performing Gaussian blurs of their renders in Adobe Photoshop and then adding a small amount of the blurred image back on top of the original. This "glow" technique softens the lighting and makes the images look less computer-generated.
It is possible to simulate diffusion in texture space (Borshukov and Lewis 2003). We can unwrap the mesh of the object with a vertex program that uses the UV texture coordinates as the screen position of the vertex. The program simply remaps the [0, 1] range of the texture coordinates to the [–1, 1] range of normalized device coordinates. We have to be careful that the object has a good UV mapping; that is, each point on the texture must map to only one point of the object, with no overlaps. By lighting this unwrapped mesh in the normal way, we obtain a 2D image representing the lighting of the object. We can then process this image and reapply it to the 3D model like a normal texture.
The vertex program in Listing 16-5 demonstrates how to render a model in UV space and perform diffuse lighting.
This technique is useful for other applications, because it decouples the shading complexity from the screen resolution: shading is performed only for each texel in the texture map, rather than for every pixel on the object. Many operations, such as convolutions, can be performed much more efficiently in image space than on a 3D surface. If the UV parameterization of the surface is relatively uniform, this is not a bad approximation, because points that are close in world space will map to points that are also close in texture space.
To simulate light diffusion in image space, we can simply blur the light map texture. We can take advantage of all the usual GPU image-processing tricks, such as using separable filters and exploiting bilinear filtering hardware. Rendering the lighting to a relatively low-resolution texture already provides a certain amount of blurring. Figure 16-5 shows an unwrapped head mesh and the results of blurring the light map texture.
Figure 16-5 Unwrapped Head Mesh
struct a2v { float4 pos : POSITION; float3 normal : NORMAL; float2 texture : TEXCOORD0; }; struct v2f { float4 hpos : POSITION; float2 texcoord : TEXCOORD0; float4 col : COLOR0; }; v2f main(a2v IN, uniform float4x4 lightMatrix) { v2f OUT; // convert texture coordinates to NDC position [-1, 1] OUT.hpos.xy = IN.texture * 2 - 1; OUT.hpos.z = 0.0; OUT.hpos.w = 1.0; // diffuse lighting float3 N = normalize(mul((float3x3) lightMatrix, IN.normal)); float3 L = normalize(-mul(lightMatrix, IN.pos).xyz); float diffuse = max(dot(N, L), 0); OUT.col = diffuse; OUT.texcoord = IN.texture; return OUT; }
A diffuse color map can also be included in the light map texture; then details from the color map will also be diffused. If shadows are included in the texture, the blurring process will result in soft shadows.
To simulate the fact that absorption and scattering are wavelength dependent, we can alter the filter weights separately for each color channel. The sample shader, shown in Listings 16-6 and 16-7, attempts to simulate skin. It takes seven texture samples with Gaussian weights. The width of the filter is greater for the red channel than for the green and blue channels, so that the red is diffused more than the other channels. The vertex program in Listing 16-6 calculates the sample positions for a blur in the x direction; the program for the y direction is almost identical. The samples are spaced two texels apart to take advantage of the bilinear filtering capability of the hardware.
v2f main(float2 tex : TEXCOORD0) { v2f OUT; // 7 samples, 2 texel spacing OUT.tex0 = tex + float2(-5.5, 0); OUT.tex1 = tex + float2(-3.5, 0); OUT.tex2 = tex + float2(-1.5, 0); OUT.tex3 = tex + float2(0, 0); OUT.tex4 = tex + float2(1.5, 0); OUT.tex5 = tex + float2(3.5, 0); OUT.tex6 = tex + float2(5.5, 0); return OUT; }
half4 main(v2fConnector v2f, uniform sampler2D lightTex ) : COLOR { // weights to blur red channel more than green and blue const float4 weight[7] = { { 0.006, 0.0, 0.0, 0.0 }, { 0.061, 0.0, 0.0, 0.0 }, { 0.242, 0.25, 0.25, 0.0 }, { 0.383, 0.5, 0.5, 0.0 }, { 0.242, 0.25, 0.20, 0.0 }, { 0.061, 0.0, 0.0, 0.0 }, { 0.006, 0.0, 0.0, 0.0 }, }; half4 a; a = tex2D(lightTex, v2f.tex0) * weight[0]; a += tex2D(lightTex, v2f.tex1) * weight[1]; a += tex2D(lightTex, v2f.tex2) * weight[2]; a += tex2D(lightTex, v2f.tex3) * weight[3]; a += tex2D(lightTex, v2f.tex4) * weight[4]; a += tex2D(lightTex, v2f.tex5) * weight[5]; a += tex2D(lightTex, v2f.tex6) * weight[6]; return a; }
To achieve a wider blur, you can either apply the blur shader several times or write a shader that takes more samples by calculating the sample positions in the fragment program. Figure 16-6 shows the blurred light map texture applied back onto the 3D head model.
Figure 16-6 Texture-Space Diffusion on a Head Model
The final shader blends the diffused lighting texture with the original high-resolution color map to obtain the final effect, as shown in Figure 16-7.
Figure 16-7 The Final Model, with Color Map
One possible extension to the depth map technique would be to render additional depth passes to account for denser objects within the object, such as bones within a body. The problem is that we are trying to account for volumetric effects using a surface-based representation. Volume rendering does not have this restriction, and it can produce much more accurate renderings of objects whose density varies. For more on volume rendering, see Chapter 39 of this book, "Volume Rendering Techniques."
Another possible extension to this technique would be to provide several color maps, each representing the color of a different layer of skin. For example, you might provide one map for the surface color and another for the veins and capillaries underneath the skin.
Greg James (2003) describes a technique that handles arbitrary polygonal objects by first adding up the distances of all the back-facing surfaces and then subtracting the distances of all the front-facing surfaces. His application computes distances in screen space for volumetric fog effects, but it could be extended to more general situations.
An interesting area of future research is combining the depth-map and texture-space techniques to obtain the best of both worlds.
The effects of subsurface scattering are an important factor in producing convincing images of skin and other translucent materials. By using several different approximations, we have shown how to achieve much of the look of subsurface scattering today in real time. As graphics hardware becomes more powerful, increasingly accurate models of subsurface light transport will be possible.
We hope that the techniques described in this chapter will inspire you to improve the realism of real-time game characters. But remember, good shading can never help bad art!
Borshukov, George, and J. P. Lewis. 2003. "Realistic Human Face Rendering for 'The Matrix Reloaded.'" SIGGRAPH 2003. Available online at http://www.virtualcinematography.org/
Everitt, Cass. 2003. "Order-Independent Transparency." Available online at http://developer.nvidia.com/view.asp?IO=order_independent_transparency
Hery, Christophe. 2002. "On Shadow Buffers." Presentation available online at http://www.renderman.org/RMR/Examples/srt2002/PrmanUserGroup2002.ppt
Hery, Christophe. 2003. "Implementing a Skin BSSRDF." RenderMan course notes, SIGGRAPH 2003. Available online at http://www.renderman.org/RMR/Books/sig03.course09.pdf.gz
James, Greg. 2003. "Rendering Objects as Thick Volumes." In ShaderX2: Shader Programming Tips & Tricks With DirectX 9, edited by Wolfgang F. Engel. Wordware. More information available online at http://www.shaderx2.com
Pharr, Matt. 2001. "Layer Media for Surface Shaders." Advanced RenderMan course notes, SIGGRAPH 2001. Available online at http://www.renderman.org/RMR/Books/sig01.course48.pdf.gz
Statue model courtesy of De Espona Infographica (http://www.deespona.com). Head model courtesy of Steven Giesler (http://www.stevengiesler.com).
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and Addison-Wesley was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.
The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.
The publisher offers discounts on this book when ordered in quantity for bulk purchases and special sales. For more information, please contact:
U.S. Corporate and Government Sales
(800) 382-3419
corpsales@pearsontechgroup.com
For sales outside of the U.S., please contact:
International Sales
international@pearsoned.com
Visit Addison-Wesley on the Web: www.awprofessional.com
Library of Congress Control Number: 2004100582
GeForce™ and NVIDIA Quadro® are trademarks or registered trademarks of NVIDIA Corporation.
RenderMan® is a registered trademark of Pixar Animation Studios.
"Shadow Map Antialiasing" © 2003 NVIDIA Corporation and Pixar Animation Studios.
"Cinematic Lighting" © 2003 Pixar Animation Studios.
Dawn images © 2002 NVIDIA Corporation. Vulcan images © 2003 NVIDIA Corporation.
Copyright © 2004 by NVIDIA Corporation.
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Printed in the United States of America. Published simultaneously in Canada.
For information on obtaining permission for use of material from this work, please submit a written request to:
Pearson Education, Inc.
Rights and Contracts Department
One Lake Street
Upper Saddle River, NJ 07458
Text printed on recycled and acid-free paper.
5 6 7 8 9 10 QWT 09 08 07
5th Printing September 2007
화면 공간 서브서피스 스캐터링 (Subsurface Scattering)
이 문서는 UE3 에서 서브서피스 스캐터링 (Subsurface Scattering) 사용법 설명서입니다. "서브서피스 스캐터링"이란 빛이 오브젝트의 표면에 투과되어 내부로 퍼져나간 후 다른 위치로 빠져나가는 현상을 말합니다.
UE3에 구현된 서브서피스 스캐터링은, 오브젝트의 표면에 입사된 빛을 표면상의 근처 다른 지점으로 블러링(번지게)하는 효과를, 화면-공간(Screen Space) 에서 구현한 것입니다. 물체 안에서 빛이 산란되며 흡수되는 과정을 본따고자, 이 블러 효과는 입사지점과 출사지점 사이의 월드-공간 거리에 따라 라이팅을 감쇠시킵니다.
이러한 추정법으로 피부같은 느낌을 내기에는 충분하지만, 보이지 않는 표면으로부터 물체에 관통되어 완전히 퍼지는 빛 산란 현상까지 잡아내지는 못합니다.
서브서피스 스캐터링을 사용하려면 DirectX11 모드에서 UE를 실행해야 합니다.
추가로 게임의 engine INI 에서도 서브서피스 스캐터링을 켜 줘야 합니다. [SystemSettings] 그룹 아래 AllowSubsurfaceScattering 부분을 찾아 True 로 설정하십시오.
머티리얼에 서브서피스 스캐터링을 추가하려면, 그 EnableSubsurfaceScattering (서브서피스 스캐터링 켜기) 프로퍼티를 참으로 설정해야 합니다:
추가로 SubsurfaceInscatteringColor, SubsurfaceAbsorptionColor, SubsurfaceScatteringRadius 머티리얼 파라미터에도 값을 넣어 줘야 합니다:
인스캐터링 컬러(내부산란색)은 오브젝트에 들어오는 빛에 변조시킬 색입니다. 표면 내부에 산란되는 색 총량을 조절하는 데 좋습니다.
초록에서 빨강으로 변하는 인스캐터링 컬러 예제는 이렇습니다:
앱섭션 컬러(흡수색)은 빛이 산란 반경에 도달한 이후에 흡수되어 남아있는 색입니다. 입사 지점의 하얀 부분에서 최대 반경 지점의 흡수색까지 지수형으로 감쇠됩니다.
인스캐터링 컬러와는 달리, 앱섭션 컬러는 빛이 오브젝트를 빠져나가는 지점에서만 값을 구합니다.
초록에서 빨강으로 변하는 앱섭션 컬러의 예제는:
스캐터링 래디어스(산란 반경)는 빛이 완전히 흡수될 때까지 이동할 수 있는 최대 거리로, 월드 유닛 단위입니다.
앱섭션 컬러처럼 빛이 오브젝트를 빠져나갈 때만 값을 구합니다.
수평 방향으로 가변적인 산란 반경 예제는:
아래 이미지는 캐릭터의 피부 느낌을 내는 데 사용된 서브서피스 스캐터링의 기본적인 예제입니다. 퐁 디퓨즈에 사용된 디퓨즈 텍스처가 있는데, 거기에 한 색을 곱해 인스캐터링 컬러를 만들어 내고 있습니다. 멋진 피부 효과를 만들어 내기 위한 꼼수라면, 퐁과 서브서피스의 라이팅 균형을 맞추는 것입니다. 서브서피스 효과가 강하면 번들번들한 젤리같은 룩이, 퐁 라이팅 쪽이 강하면 캐릭터가 너무 마르고 거칠어 보일 것입니다.
directx 사원수 관련 함수 d3dx9math.h (0) | 2012.11.02 |
---|---|
GPU 에서 노멀맵 굽기 (0) | 2012.11.02 |
D3DXMatrixTransformation2D (0) | 2012.11.02 |
로컬좌표와 월드 좌표 (0) | 2012.11.02 |
라디오시티 (0) | 2012.11.02 |
D3DXMATRIX* WINAPI D3DXMatrixTransformation2D(
D3DXMATRIX *pOut, //연산 결과 행렬
CONST D3DXVECTOR2* pScalingCenter, //스케일 중심점 지정, NULL(=0,0), 0,0 이미지 왼쪽 상단인듯
FLOAT ScalingRotation, //확대 회전 요소를 지정, pScaling 의 x,y의 확대비율이 다른 경우에
//영향을 준다.
CONST D3DXVECTOR2* pScaling, //확대율을 지정 , NULL 은 스케일 하지 않음
CONST D3DXVECTOR2* pRotationCenter, //회전 중심점 NULL(=0,0)
FLOAT Rotation, //회전 각도(Radian 단위), 우회전 기준
CONST D3DXVECTOR2* pTranslation //평행이동 NULL(=0,0)
);
Module: wine Branch: master Commit: 09c6caea062c4aeaaea874bf6805ba31afdce39d URL: http://source.winehq.org/git/wine.git/?a=commit;h=09c6caea062c4aeaaea874bf6805ba31afdce39d Author: David Adam <david.adam.cnrs at gmail.com> Date: Wed Nov 12 19:16:30 2008 +0100 d3dx8: Implement D3DXMatrixTransformation2D. --- dlls/d3dx9_36/d3dx9_36.spec | 2 +- dlls/d3dx9_36/math.c | 80 +++++++++++++++++++++++++++++++++++++++++++ dlls/d3dx9_36/tests/math.c | 79 ++++++++++++++++++++++++++++++++++++++++++ include/d3dx9math.h | 1 + 4 files changed, 161 insertions(+), 1 deletions(-) diff --git a/dlls/d3dx9_36/d3dx9_36.spec b/dlls/d3dx9_36/d3dx9_36.spec index 9058dd7..d7fba36 100644 --- a/dlls/d3dx9_36/d3dx9_36.spec +++ b/dlls/d3dx9_36/d3dx9_36.spec @@ -225,7 +225,7 @@ @ stdcall D3DXMatrixScaling(ptr long long long) d3dx8.D3DXMatrixScaling @ stdcall D3DXMatrixShadow(ptr ptr ptr) d3dx8.D3DXMatrixShadow @ stdcall D3DXMatrixTransformation(ptr ptr ptr ptr ptr ptr ptr) d3dx8.D3DXMatrixTransformation -@ stub D3DXMatrixTransformation2D +@ stdcall D3DXMatrixTransformation2D(ptr ptr long ptr ptr long ptr) @ stdcall D3DXMatrixTranslation(ptr long long long) d3dx8.D3DXMatrixTranslation @ stdcall D3DXMatrixTranspose(ptr ptr) d3dx8.D3DXMatrixTranspose @ stub D3DXOptimizeFaces diff --git a/dlls/d3dx9_36/math.c b/dlls/d3dx9_36/math.c index b5cdde2..a00ffc5 100644 --- a/dlls/d3dx9_36/math.c +++ b/dlls/d3dx9_36/math.c @@ -1,6 +1,7 @@ /* * Mathematical operations specific to D3DX9. * + * Copyright (C) 2008 David Adam * Copyright (C) 2008 Philip Nilsson * * This library is free software; you can redistribute it and/or @@ -130,6 +131,85 @@ HRESULT WINAPI D3DXMatrixDecompose(D3DXVECTOR3 *poutscale, D3DXQUATERNION *poutr } /************************************************************************* + * D3DXMatrixTransformation2D + */ +D3DXMATRIX* WINAPI D3DXMatrixTransformation2D( + D3DXMATRIX *pout, CONST D3DXVECTOR2 *pscalingcenter, + FLOAT scalingrotation, CONST D3DXVECTOR2 *pscaling, + CONST D3DXVECTOR2 *protationcenter, FLOAT rotation, + CONST D3DXVECTOR2 *ptranslation) +{ + D3DXQUATERNION rot, sca_rot; + D3DXVECTOR3 rot_center, sca, sca_center, trans; + + if ( pscalingcenter ) + { + sca_center.x=pscalingcenter->x; + sca_center.y=pscalingcenter->y; + sca_center.z=0.0f; + } + else + { + sca_center.x=0.0f; + sca_center.y=0.0f; + sca_center.z=0.0f; + } + + if ( pscaling ) + { + sca.x=pscaling->x; + sca.y=pscaling->y; + sca.z=0.0f; + } + else + { + sca.x=0.0f; + sca.y=0.0f; + sca.z=0.0f; + } + + if ( protationcenter ) + { + rot_center.x=protationcenter->x; + rot_center.y=protationcenter->y; + rot_center.z=0.0f; + } + else + { + rot_center.x=0.0f; + rot_center.y=0.0f; + rot_center.z=0.0f; + } + + if ( ptranslation ) + { + trans.x=ptranslation->x; + trans.y=ptranslation->y; + trans.z=0.0f; + } + else + { + trans.x=0.0f; + trans.y=0.0f; + trans.z=0.0f; + } + + rot.w=cos(rotation/2.0f); + rot.x=0.0f; + rot.y=0.0f; + rot.z=sin(rotation/2.0f); + + sca_rot.w=cos(scalingrotation/2.0f); + sca_rot.x=0.0f; + sca_rot.y=0.0f; + sca_rot.z=sin(scalingrotation/2.0f); + + D3DXMatrixTransformation(pout, &sca_center, &sca_rot, &sca, &rot_center, &rot, &trans); + + return pout; +} + +/************************************************************************* * D3DXPlaneTransformArray */ D3DXPLANE* WINAPI D3DXPlaneTransformArray( diff --git a/dlls/d3dx9_36/tests/math.c b/dlls/d3dx9_36/tests/math.c index 2930d34..cc71b33 100644 --- a/dlls/d3dx9_36/tests/math.c +++ b/dlls/d3dx9_36/tests/math.c @@ -1,4 +1,5 @@ /* + * Copyright 2008 David Adam * Copyright 2008 Philip Nilsson * * This library is free software; you can redistribute it and/or @@ -584,6 +585,83 @@ static void test_Matrix_Decompose(void) ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %x\n", hr); } +static void test_Matrix_Transformation2D(void) +{ + D3DXMATRIX exp_mat, got_mat; + D3DXVECTOR2 rot_center, sca, sca_center, trans; + FLOAT rot, sca_rot; + + rot_center.x = 3.0f; + rot_center.y = 4.0f; + + sca.x = 12.0f; + sca.y = -3.0f; + + sca_center.x = 9.0f; + sca_center.y = -5.0f; + + trans.x = -6.0f; + trans.y = 7.0f; + + rot = D3DX_PI/3.0f; + + sca_rot = 5.0f*D3DX_PI/4.0f; + + exp_mat.m[0][0] = -4.245192f; + exp_mat.m[1][0] = -0.147116f; + exp_mat.m[2][0] = 0.0f; + exp_mat.m[3][0] = 45.265373f; + exp_mat.m[0][1] = 7.647113f; + exp_mat.m[1][1] = 8.745192f; + exp_mat.m[2][1] = 0.0f; + exp_mat.m[3][1] = -13.401899f; + exp_mat.m[0][2] = 0.0f; + exp_mat.m[1][2] = 0.0f; + exp_mat.m[2][2] = 0.0f; + exp_mat.m[3][2] = 0.0f; + exp_mat.m[0][3] = 0.0f; + exp_mat.m[1][3] = 0.0f; + exp_mat.m[2][3] = 0.0f; + exp_mat.m[3][3] = 1.0f; + + D3DXMatrixTransformation2D(&got_mat, &sca_center, sca_rot, &sca, &rot_center, rot, &trans); + + expect_mat(&exp_mat, &got_mat); + +/*_________*/ + + sca_center.x = 9.0f; + sca_center.y = -5.0f; + + trans.x = -6.0f; + trans.y = 7.0f; + + rot = D3DX_PI/3.0f; + + sca_rot = 5.0f*D3DX_PI/4.0f; + + exp_mat.m[0][0] = 0.0f; + exp_mat.m[1][0] = 0.0f; + exp_mat.m[2][0] = 0.0f; + exp_mat.m[3][0] = 2.830127f; + exp_mat.m[0][1] = 0.0f; + exp_mat.m[1][1] = 0.0f; + exp_mat.m[2][1] = 0.0f; + exp_mat.m[3][1] = 12.294229f; + exp_mat.m[0][2] = 0.0f; + exp_mat.m[1][2] = 0.0f; + exp_mat.m[2][2] = 0.0f; + exp_mat.m[3][2] = 0.0f; + exp_mat.m[0][3] = 0.0f; + exp_mat.m[1][3] = 0.0f; + exp_mat.m[2][3] = 0.0f; + exp_mat.m[3][3] = 1.0f; + + D3DXMatrixTransformation2D(&got_mat, &sca_center, sca_rot, NULL, NULL, rot, &trans); + + expect_mat(&exp_mat, &got_mat); +} + static void test_D3DXVec_Array(void) { unsigned int i; @@ -725,5 +803,6 @@ START_TEST(math) { test_Matrix_AffineTransformation2D(); test_Matrix_Decompose(); + test_Matrix_Transformation2D(); test_D3DXVec_Array(); } diff --git a/include/d3dx9math.h b/include/d3dx9math.h index 9992919..83b8749 100644 --- a/include/d3dx9math.h +++ b/include/d3dx9math.h @@ -296,6 +296,7 @@ D3DXMATRIX* WINAPI D3DXMatrixRotationZ(D3DXMATRIX *pout, FLOAT angle); D3DXMATRIX* WINAPI D3DXMatrixScaling(D3DXMATRIX *pout, FLOAT sx, FLOAT sy, FLOAT sz); D3DXMATRIX* WINAPI D3DXMatrixShadow(D3DXMATRIX *pout, CONST D3DXVECTOR4 *plight, CONST D3DXPLANE *pPlane); D3DXMATRIX* WINAPI D3DXMatrixTransformation(D3DXMATRIX *pout, CONST D3DXVECTOR3 *pscalingcenter, CONST D3DXQUATERNION *pscalingrotation, CONST D3DXVECTOR3 *pscaling, CONST D3DXVECTOR3 *protationcenter, CONST D3DXQUATERNION *protation, CONST D3DXVECTOR3 *ptranslation); +D3DXMATRIX* WINAPI D3DXMatrixTransformation2D(D3DXMATRIX *pout, CONST D3DXVECTOR2 *pscalingcenter, FLOAT scalingrotation, CONST D3DXVECTOR2 *pscaling, CONST D3DXVECTOR2 *protationcenter, FLOAT rotation, CONST D3DXVECTOR2 *ptranslation); D3DXMATRIX* WINAPI D3DXMatrixTranslation(D3DXMATRIX *pout, FLOAT x, FLOAT y, FLOAT z); D3DXMATRIX* WINAPI D3DXMatrixTranspose(D3DXMATRIX *pout, CONST D3DXMATRIX *pm);
GPU 에서 노멀맵 굽기 (0) | 2012.11.02 |
---|---|
표면 스케터링으로 실시간 근사 (0) | 2012.11.02 |
로컬좌표와 월드 좌표 (0) | 2012.11.02 |
라디오시티 (0) | 2012.11.02 |
에르미트 (0) | 2012.11.02 |
http://ljh131.cafe24.com/beeswing/zbxe/30358
공 10개가 막 튕기는 예제를 만든다고 하죠...
3D 그래픽맨에게...'공한개만 맹글어주세염.' 이라고 하죠.
그럼 그래픽맨은 십중팔구 원점을 중심으로한 구를 만들어 줍니다.
그럼 이걸 가지고 월드에 뿌릴려면...
공의 중심점 목록이 있겠죠? ball[n].xyz
근디..공은 수십개의 점으로 이루어져 있습니다. ball[n].vertex[i]
ball[n].vertex[i]는 그래픽맨이 실제로 만들어준 데이터입니다.
근데..이 점들은...구의 중심을 원점이라 했을때의 상대 좌표죠?
이때 ball[n].vertex[i]의 좌표계를 '로컬좌표'라고 합니다.
이때, ball[n].vertex[i]는 모든 공이 같은값을 가짐니다.
(그래픽맨이 한개밖에 안만들어 주었으니..)
공유해도 된다는 말이죠. (같은 모양의 캐릭터가 수십마리 나올때..^^)
그럼 실제 공을 화면에 찍으려면 모든 점을 실제 위치에 옮겨놔야 겠죠.
ball[n].world_vertex[i].xyz = ball[n].vertex[i].xyz + ball[n].xyz;
ball[n].world_vertex[i]는 실제 각 버텍스들의 월드상의 위치.
즉 '월드좌표'입니다.
실제 그물체가 있어야 할곳으로 이동/회전 시켜준다라는 말은 바로
위에서 했던 작업을 말하는겁니다. ( 로컬좌표->월드좌표 )
프로그램상에선..카메라가 왼쪽으로 움직일때 월드상에 객체들을
오른쪽으로 재 배치 시킵니다. 뿐만아니라 왼쪽으로 돌면 물체들을
오른쪽으로 돌려놓고...
뭐...개념상 그렇다는거죠...이렇게 이야기하나 저렇게 이야기 하나..
결국 최종적으로 나오는 공식은 마찬가지 입니다.
이해가 되셨기를...
감자 성수올림...@~
표면 스케터링으로 실시간 근사 (0) | 2012.11.02 |
---|---|
D3DXMatrixTransformation2D (0) | 2012.11.02 |
라디오시티 (0) | 2012.11.02 |
에르미트 (0) | 2012.11.02 |
Z-Fighting (0) | 2012.11.02 |
Lighting and shadow casting algorithms can be very roughly divided into two categories; Direct Illumination and Global Illumination. Many people will be familiar with the former category, and the problems associated with it. This article will briefly discuss the two approaches, then give an in-depth study of one Global Illumination method, Radiosity.
There are all sorts of techniques under this heading: Shadow Volumes, Z-Buffer methods, Ray Tracing . . . But as a general rule, they all suffer from similar problems, and all require some kind of fudge in order to overcome them.
|
It it quite common for people to claim that ray tracers and other renderers produce 'photo-realistic' results. But imagine someone were to show you a typical ray traced image, and claim it was a photo. You would claim in return that they were blind or lying.
It should also be noted that, in the real world, it is still possible to see objects that are not directly lit; shadows are never completely black. Direct Illumination renderers try to handle such situations by adding an Ambient Light term. Thus all objects receive a minimum amount of uni-directional light.
|
Lighting a simple scene with Direct LightingI modeled this simple scene in 3D Studio. I wanted the room to look as if it was lit by the sun shining in through the window. So, I set up a spotlight to shine in. When I rendered it, the entire room was pitch black, except for a couple of patches on the floor that the light reached. | |
Lighting a simple scene with Global LightingI modeled the same scene in my own radiosity renderer. To provide the source of light, I rendered an image of the sky withTerragen, and placed it outside the window. No other source of light was used.With no further effort on my part, the room looks realistically lit.
|
I would now like to ask an expert on shadows, who will explain to you everything they know about the subject. My expert is a tiny patch of paint on the wall in front of me.
hugo: "Why is it that you are in shadow, when a very similar patch of paint near you is in light?"
paint: "What do you mean?"
hugo: "How is it you know when to be in shadow, and when not to be? What do you know about shadow casting algorithms? You're just some paint."
paint: "Listen mate. I don't know what you're talking about. My job is a simple one: any light that hits me, I scatter back."
hugo: "Any light?"
paint: "Yes, any light at all. I don't have a preference."
So there you have it. The basic premise of Radiosity. Any light that hits a surface is reflected back into the scene. That's any light. Not just light that's come directly from light sources. Any light. That's how paint in the real world thinks, and that's how the radiosity renderer will work.
In my next article, I will be explaining how you can make your own talking paint.
So, the basic principal behind the radiosity renderer is to remove the distinction between objects and light sources. Now, you can consider everything to be a potential light source.
Anything that is visible is either emitting or reflecting light, i.e. it is a source of light. A Light Source. Everything you can see around you is a light source. And so, when we are considering how much light is reaching any part of a scene, we must take care to add up light from all possible light sources.
Now that you have the important things in mind. I will take you through the process of performing Radiosity on a scene.
A Simple SceneWe begin with a simple scene: a room with three windows. There are a couple of pillars and some alcoves, to provide interesting shadows.It will be lit by the scenery outside the windows, which I will assume is completely dark, except for a small, bright sun. |
Now, lets choose one of the surfaces in the room, and consider the lighting on it. |
As with many difficult problems in computer graphics, we'll divide it up into little patches (of paint), and try to see the world from their point of view. From now on I'll refer to these patches of paint simply as patches. |
Take one of those patches. And imagine you are that patch. What does the world look like from that perspective? |
View from a patchPlacing my eye very carefully on the patch, and looking outwards, I can see what it sees. The room is very dark, because no light has entered yet. But I have drawn in the edges for your benefit.By adding together all the light it sees, we can calculate the total amount of light from the scene reaching the patch. I'll refer to this as the total incident light from now on. This patch can only see the room and the darkness outside. Adding up the incident light, we would see that no light is arriving here. This patch is darkly lit. |
View from a lower patchPick a patch a little further down the pillar. This patch can see the bright sun outside the window. This time, adding up the incident light will show that a lot of light is arriving here (although the sun appears small, it is very bright). This patch is brightly lit. |
Lighting on the PillarHaving repeated this process for all the patches, and added up the incident light each time, we can look back at the pillar and see what the lighting is like.The patches nearer the top of the pillar, which could not see the sun, are in shadow, and those that can are brightly lit. Those that could see the sun partly obscured by the edge of the window are only dimly lit. And so Radiosity proceeds in much the same fashion. As you have seen, shadows naturally appear in parts of the scene that cannot see a source of light. |
Entire Room Lit: 1st PassRepeating the process for every patch in the room, gives us this scene. Everything is completely dark, except for surfaces that have received light from the sun.So, this doesn't look like a very well lit scene. Ignore the fact that the lighting looks blocky; we can fix that by using many more patches. What's important to notice is that the room is completely dark, except for those areas that can see the sun. At the moment it's no improvement over any other renderer. Well, it doesn't end here. Now that some parts of the room are brightly lit, they have become sources of light themselves, and could well cast light onto other parts of the scene. |
View from the patch after 1st PassPatches that could not see the sun, and so received no light, can now see the light shining on other surfaces. So in the next pass, this patch will come out slightly lighter than the completely black it is now. |
Entire Room Lit: 2nd PassThis time, when you calculate the incident light on each patch in the scene, many patches that were black before are now lit. The room is beginning to take on a more realistic appearance.What's happened is that sun light has reflected once from the floor and walls, onto other surfaces. |
Entire Room Lit: 3rd PassThe third pass produces the effect of light having reflected twice in the scene. Everything looks pretty much the same, but is slightly brighter.The next pass only looks a little brighter than the last, and even the 16 th is not a lot different. There's not much point in doing any more passes after that. The radiosity process slowly converges on a solution. Each pass is a little less different than the last, until eventually it becomes stable. Depending on the complexity of the scene, and the lightness of the surfaces, it may take a few, or a few thousand passes. It's really up to you when to stop it, and call it done. |
4th Pass | 16th Pass |
Emmision
Though I have said that we'll consider lightsources and objects to be basically the same, there must obviously be some source of light in the scene. In the real world, some objects do emit light, and some don't, and all objects absorb light to some extent. We must somehow distinguish between parts of the scene that emit light, and parts that don't. We shall handle this in radiosity by saying that all patches emit light, but for most patches, their light emmision is zero. This property of a patch, I'll call emmision.
Reflectance
When light hits a surface, some light is absorbed and becomes heat, (we can ignore this) and the rest is reflected. I'll call the proportion of light reflected by a patchreflectance.
Incident and Excident Light
During each pass, it will be necessary to remember two other things, how much light is arriving at each patch, and how much light is leaving each patch. I'll call these two, incident_light and excident_light. The excident light is the visible property of a patch. When we look at a patch, it is the excident light that we're seeing.
incident_light = sum of all light that a patch can see excident_light = (incident_light*reflectance) + emmision
Patch structure
Now that we know all the necessary properties of a patch, it's time to define a patch. Later, I'll explain the details of the four variables.
structure PATCH emmision reflectance incident excident end structure
Now that I've explained the basics of the algorithm, I'll tell it again in pseudocode form, to make it concrete. Clearly this is still quite high level, but I'll explain in more detail later.
| Explanation of Code initialise patches: Passes Loop: each patch collects light from the scene calculate excident light from each patch: This process must be repeated many times to get a good effect. If the renderer needs another pass, then we jump back to Passes_Loop. |
The Hemisphere Imagine a fish eye view wrapped onto a hemisphere. Place the hemisphere over a patch (left: red square), and from that patch's point of view, the scene wrapped on the inside of the hemisphere looks just like the scene from it's point of view. There's no difference. Placing a camera in the middle of the hemisphere, you can see that the view looks just like any other rendering of the scene (right). If you could find a way to render a fisheye view easily, then you could just sum up the brightness of every pixel to calculate the total incident light on the patch. However, it's not easy to render a fisheye view, and so some other way must be found to calculate the incident light. | Rendering from the centre of the hemisphere |
The Hemicube Surprisingly (or unsurprisingly, depending on how mathematical you are) a hemicube looks exactly the same as a hemisphere from the patch's point of view. | Rendering from the centre of the hemicube |
So, you can easily produce each of these images by placing a camera on a patch, and render it pointing forwards, up, down, left and right. The four side images are, of course, cut in half, and so, only half a rendering is required there.
This is view of 3 spheres, rendered with a 90° field of view. All three spheres are the same distance from the camera, but because of the properties of perspective transformation, objects at the edge of the image appear spretched and larger than ones in the middle. If this was the middle image of a hemicube, and the three spheres were light sources, then those near the edge would cast more light onto the patch than they should. This would be inaccurate, and so we must compensate for this. If you were to use a hemicube to calculate the total incident light falling on a patch, and just added together the values of all the pixel rendered in the hemicube, you would be giving an unfair weight to objects lying at the corners of the hemicube. They would appear to cast more light onto the patch. To compensate for this, it is necessary to 'dim' the pixels at the edges and corners, so that all objects contribute equally to the incident light, no matter where they may lie in the hemicube. Rather than give a full explanation, I'm just going to tell you how this is done. | |
Pixels on a surface of the hemicube are multiplied by the cosine of the angle between the direction the camera is facing in, and the line from the camera to the pixel. On the left is an image of the map used to compensate for the distortion. (shown half size relative to the image above) |
Any budding graphics programmer knows Lambert's cosine law: The apparent brightness of a surface is proportional to the cosine of the angle between the surface normal, and the direction of the light. Therefore, we should be sure to apply the same law here. This is simply done by multiplying pixels on the hemicube by the relevant amount. On the left is an image of the map used to apply Lambert's law to the hemicube. White represents the value 1.0, and black represents the value 0.0. (shown half size relative to the image above) |
Now pay attention, this is important: Multiplying the two maps together gives this. This map is essential for producing an accurate radiosity solution. It is used to adjust for the perspective distortion, mentioned above, that causes objects near the corners of the hemicubes to shine too much light onto a patch. It also gives you Lambert's Cosine Law. Having created this map, you should have the value 1.0 right at the centre, and the value 0.0 at the far corners. Before it can be used, the map must be normalised. The sum of all pixels in the map should be 1.0.
|
First, it renders the 5 faces of the hemicube using the procedure RenderView(point, vector, part). This procedure takes as it's arguments a point, telling it where the camera should be for the rendering, a vector, telling it what direction the camera should be pointing in, and another argument telling it which part of the final image should be rendered. These 5 images are stored in hemicube structure called H (left column of images below).
Once the hemicube H has been rendered, it is multiplied by the multiplier hemicube M (middle column of images below), and the result is stored in the hemicube R(right column of images below).
Then the total value of the light in R is added up and divided by the number of pixels in a hemicube. This should give the total amount of light arriving at the point in question.
|
structure light float Red float Green float Blue end structure
hemicube: used for storing the view of a scene from the point of view of some point in the scene. A Hemicube would consist of five images, as illustrated above, where each pixel was of type light. In the case of the Multiplier Hemicube, what is stored is not a value of light, but some multiplier value less than 1.0, as illustrated above.
structure hemicube image front image up image down image left image right end structurecamera: for example
structure camera point lens vector direction end structure
Fortunately, this is something people have been doing since the dawn of time. Um, since the dawn of the raster display, and since then there has been much work put into rendering texture mapped scenes as fast as possible. I won't go into a whole lot of detail here, I'm really not the person best qualified to be talking about optimised rendering. My own renderer is so slow you have to use cussing words to describe it. The algorithm also lends itself well to optimisation with standard 3D graphics hardware, though you have do some fiddling and chopping to get it to render (3x32) bit textures.
The speed improvement I'm going to discuss in this article does not concern optimising the actual rendering of the hemicubes, but rather reducing the number of hemicubes that need to be rendered. You will, of course, have noticed that the light maps illustrated in the black and white renderings above were somewhat blocky, low resolution. Don't fear, their resolution can be increased as far as you want.
Take a look at the surface on the left, outlined in red. The lighting is basically very simple, there's a bright bit, and a less bright bit, with a fairly sharp edge between the two. To reproduce the edge sharply, you would normally need a high resolution light map and, therefore, have to render very many hemicubes. But it hardly seems worthwhile rendering so many hemicubes just to fill in the bright or less-bright areas which are little more than solid colour. It would be more worthwhile to render a lot of hemicubes near the sharp edge, and just a few in the other areas. Well, it is possible, and quite straightforward. The algorithm I will describe below will render a few hemicubes scattered across the surface, then render more near the edges, and use linear interpolation to fill in the rest of the light map. |
The Algorithm: On the far left you can see the light map in the process of being generated. Next to it, you can see which pixels were produced using a hemicube (red) and which were linearly interpolated (green). | ||
1: Use a hemicube to calculate every 4th pixel. I'll show these pixels on the right as . | ||
2: Pass Type 1: Examine the pixels which are horizontally or vertically halfway between previously calculated pixels . If the neighbouring pixels differ by more than some threshold amount, then calculate this pixel using a hemicube, otherwise, interpolate from the neighbouring pixels. | ||
3: Pass Type 2: Examine the pixels which are in the middle of a group of 4 pixels. If the neighbours differ by much, then use a hemicube for this pixel, otherwise use linear interpolation. | ||
4: Pass Type 1: Same as step 2, but with half the spacing. | ||
5: Pass Type 2: Same as step 3, but with half the spacing. |
You should be able to see, from the maps on the left, that most of the light map was produced using linear interpolation. In fact, from a total of 1769 pixels, only 563 were calculated by hemicube, and 1206 by linear interpolation. Now, since rendering a hemicube takes a very long time indeed, compared to the negligable time required to do a linear interpolation, it represents a speed improvement of about 60% !
Now, this method is not perfect, and it can occasionally miss very small details in a light map, but it's pretty good in most situations. There's a simple way to help it catch small details, but I'll leave that up to your own imagination.
#### CODE EDITING IN PROGRESS - BIT MESSY STILL #### float ratio2(float a, float b) { if ((a==0) && (b==0)) return 1.0; if ((a==0) || (b==0)) return 0.0; if (a>b) return b/a; else return a/b; } float ratio4(float a, float b, float c, float d) { float q1 = ratio2(a,b); float q2 = ratio2(c,d); if (q1<q2) return q1; else return q2; } procedure CalcLightMap() vector normal = LightMap.Surface_Normal float Xres = LightMap.X_resolution float Yres = LightMap.Y_resolution point3D SamplePoint light I1, I2, I3, I4 Accuracy = Some value greater than 0.0, and less than 1.0. Higher values give a better quality Light Map (and a slower render). 0.5 is ok for the first passes of the renderer. 0.98 is good for the final pass. Spacing = 4 Higher values of Spacing give a slightly faster render, but will be more likely to miss fine details. I find that 4 is a pretty reasonable compromise. // 1: Initially, calculate an even grid of pixels across the Light Map. // For each pixel calculate the 3D coordinates of the centre of the patch that // corresponds to this pixel. Render a hemicube at that point, and add up // the incident light. Write that value into the Light Map. // The spacing in this grid is fixed. The code only comes here once per Light // Map, per render pass. for (y=0; y<Yres; y+=Spacing) for (x=0; x<Xres; x+=Spacing) { SamplePoint = Calculate coordinates of centre of patch incidentLight = Calc_Incident_Light(SamplePoint, normal) LightMap[x, y] = incidentLight } // return here when another pass is required Passes_Loop: threshold = pow(Accuracy, Spacing) // 2: Part 1. HalfSpacing = Spacing/2; for (y=HalfSpacing; y<=Yres+HalfSpacing; y+=Spacing) { for (x=HalfSpacing; x<=Xres+HalfSpacing; x+=Spacing) { // Calculate the inbetween pixels, whose neighbours are above and below this pixel if (x<Xres) // Don't go off the edge of the Light Map now { x1 = x y1 = y-HalfSpacing // Read the 2 (left and right) neighbours from the Light Map I1 = LightMap[x1+HalfSpacing, y1] I2 = LightMap[x1-HalfSpacing, y1] // If the neighbours are very similar, then just interpolate. if ( (ratio2(I1.R,I2.R) > threshold) && (ratio2(I1.G,I2.G) > threshold) && (ratio2(I1.B,I2.B) > threshold) ) { incidentLight.R = (I1.R+I2.R) * 0.5 incidentLight.G = (I1.G+I2.G) * 0.5 incidentLight.B = (I1.B+I2.B) * 0.5 LightMap[x1, y1] = incidentLight } // Otherwise go to the effort of rendering a hemicube, and adding it all up. else { SamplePoint = Calculate coordinates of centre of patch incidentLight = Calc_Incident_Light(SamplePoint, normal) LightMap[x1, y1] = incidentLight } } // Calculate the inbetween pixels, whose neighbours are left and right of this pixel if (y<Yres) // Don't go off the edge of the Light Map now { x1 = x-HalfSpacing y1 = y // Read the 2 (up and down) neighbours from the Light Map I1 = LightMap[x1,y1-HalfSpacing]; I2 = LightMap[x1,y1+HalfSpacing]; // If the neighbours are very similar, then just interpolate. if ( (ratio2(I1.R,I2.R) > threshold) && (ratio2(I1.G,I2.G) > threshold) && (ratio2(I1.B,I2.B) > threshold) ) { incidentLight.R = (I1.R+I2.R) * 0.5 incidentLight.G = (I1.G+I2.G) * 0.5 incidentLight.B = (I1.B+I2.B) * 0.5 LightMap[x1,y1] = incidentLight } // Otherwise go to the effort of rendering a hemicube, and adding it all up. else { SamplePoint = Calculate coordinates of centre of patch incidentLight = Calc_Incident_Light(SamplePoint, normal) LightMap[x1, y1] = incidentLight } }//end if }//end x loop }//end y loop // 3: Part 2 // Calculate the pixels, whose neighbours are on all 4 sides of this pixel for (y=HalfSpacing; y<=(Yres-HalfSpacing); y+=Spacing) { for (x=HalfSpacing; x<=(Xres-HalfSpacing); x+=Spacing) { I1 = LightMap[x, y-HalfSpacing] I2 = LightMap[x, y+HalfSpacing] I3 = LightMap[x-HalfSpacing, y] I4 = LightMap[x+HalfSpacing, y] if ( (ratio4(I1.R,I2.R,I3.R,I4.R) > threshold) && (ratio4(I1.G,I2.G,I3.G,I4.G) > threshold) && (ratio4(I1.B,I2.B,I3.B,I4.B) > threshold) ) { incidentLight.R = (I1.R + I2.R + I3.R + I4.R) * 0.25 incidentLight.G = (I1.G + I2.G + I3.G + I4.G) * 0.25 incidentLight.B = (I1.B + I2.B + I3.B + I4.B) * 0.25 LightMap[x,y] = incidentLight } else { SamplePoint = Calculate coordinates of centre of patch incidentLight = Calc_Incident_Light(SamplePoint, normal) LightMap[x, y] = incidentLight; } } } Spacing = Spacing / 2 Stop if Spacing = 1, otherwise go to Passes_Loop |
It is generally considered that Radiosity does not deal well with point light sources. This is true to some extent, but it is not impossible to have reasonable point light sources in your scene. I tried adding bright, point sized objects to my scenes, that were rendered as wu-pixels. When a hemicube was rendered, they would appear in the hemicube as bright points, thus shining light onto patches. They almost worked, but were subject to some unacceptable artifacts. The scene on the right was lit by three point spot lights; two on the pillars at the back, and one near the top-left, pointing towards the camera. The scene appears fine from this angle, but nasty artifacts are apparent if I turn the camera around. | |
You can see, on the bottom image, three dark lines along the wall and floor. These were caused by the the light source seeming to get lost at the very edges of the hemicubes. Perhaps this wouldn't have been so bad if I'd got my maths absolutely perfect and the edges of the hemicubes matched perfectly, but I'm sure that there would still have been noticable artifacts. So, rather than rendering the point lights onto the hemicubes, you can use ray tracing to cast the light from point sources onto patches. |
How you go about this optimisation might not be quite what you expect, but it works well, letting the CPU and rendering hardware work together in parallel. The hardware handles the texture mapping and hidden surface removal (z-buffering), and the CPU handles the rest of the radiosity.
As far as I know, there is no rendering hardware that deals with floating point lighting values, or even lighting values above 255. So there is no point trying to get them to directly render scenes with such lighting. However, with a little subtlety, you can get them to do the texture mapping and hidden surface removal, while you put the lighting back in with a simple, fast loop.
If 3D hardware can write 32-bit pixels to the screen, then it can be made to write 32-bit values representing anything we want. 3D hardware can't write actual floating point RGBs to the screen, but it can write 32-bit pointers to the patches that should be rendered there. Once it's done that, you simply need to take each pixel, and use it's 32-bit value as an address to locate the patch that should have been rendered there.
Here is one of the patch maps from the scene above. Each pixel has a floating point value for Red, Green and Blue. And so 3D hardware will not be able to deal with this directly. | Now this is another map. It looks totally weird, but ignore how it looks for now. Each pixel in this map is actually a 32-bit value, which is the address of the corresponding pixel on the left. The reason the colours appear is because the lowest three bytes in the address are interpreted as colours. |
Once you make a whole set of these pointer textures (one for each surface in your scene), you can give them to the 3D hardware to render with them. The scene it comes out with will look something like this (right). The scene looks totally odd, but you can make out surfaces covered with patterns similar to the one above. The pixels should not be interpreted as colours, but as pointers. If your graphics card used 32-bit textures, then they will be in a form something like ARGB, with A, R G and B being 8-bit values. Ignore this structure and treat each pixel as a 32-bit value. Use them as memory pointers back to the patches that should be there, and recreate the scene properly with patches. Important: You must make sure that you render the scene purely texture mapped. That means: NO linear interpolation, NO anti-aliasing, NO motion blur, NO shading/lighting, NO Mip Mapping, NO Fog, NO Gamma Correction or anything else that isn't just a straight texture map. If you do not do this, the adresses produced will not point to the correct place, and your code will almost certainally crash. |
Your average monitor can at best produce only dim light, not a lot brighter than a surface indoors. Clearly you cannot display your image directly on a monitor. To do this would require a monitor that could produce light as bright as the sun, and a graphics card with 32 bits per channel. These things don't exist for technical, not to mention safety, issues. So what can you do?
Most people seem to be happy to look at photographs and accept them as faithful representations of reality. They are wrong. Photographs are no better than monitors for displaying real-life bright images. Photographs cannot give off light as bright as the sun, but people never question their realism. Now this is where confusion sets in.
Try this: Go out in a totally overcast day. Stand infront of something white. If you look at the clouds, you will see them as being grey, but look at the white object, and it appears to be white. So what? Well the white thing is lit by the grey clouds and so can't possibly be any brighter than them (in fact it will be darker), and yet we still perceive it to be white. If you don't believe me, take a photo showing the white thing and the sky in the background. You will see that the white thing looks darker than the clouds.
Don't trust your eyes: They are a hell of a lot smarter than you are.
So what can you do? Well, since people are so willing to accept photographs as representations of reality we can take the output of the renderer, which is a physical model of the light in a scene, and process this with a rough approximation of a camera film. I have already written an article on this: Exposure, so I will say no more about it here.
References
The Solid Map: Methods for Generating a 2쵥 Texture Map for Solid Texturing: http://graphics.cs.uiuc.edu/~jch/papers/pst.pdf
This paper will be very useful if you are going to try to implement your own radiosity renderer. How do you apply a texture map evenly, and without distortion across some arbitary polygonal object? A radiosity renderer will need to do this.
Helios32: http://www.helios32.com/
Offers a platform-independent solution for developers looking for radiosity rendering capabilities.
Radiosity In English: http://www.flipcode.com/tutorials/tut_rad.shtml
As the title suggests this is an article about Radiosity, written using English words. I didn't understand it.
Real Time Radiosity: http://www.gamedev.net/reference/programming/features/rtradiosity2/
That sounds a little more exciting. There doesn't seem to be a demo though.
An Empirical Comparison of Radiosity Algorithms: http://www.cs.cmu.edu/~radiosity/emprad-tr.html
A good technical article comparing matrix, progressive, and wavelet radiosity algorithms. Written by a couple of the masters.
A Rapid Hierarchical Radiosity Algorithm: http://graphics.stanford.edu/papers/rad/
A paper that presents a rapid hierarchical radiosity algorithm for illuminating scenes containing large polygonal patches.
KODI's Radiosity Page : http://ls7-www.informatik.uni-dortmund.de/~kohnhors/radiosity.html
A whole lot of good radiosity links.
Graphic Links: http://web.tiscalinet.it/GiulianoCornacchiola/Eng/GraphicLinks6.htm
Even more good links.
Rover: Radiosity for Virtual Reality Systems: http://www.scs.leeds.ac.uk/cuddles/rover/
*Very Good* A thesis on Radiosity. Contains a large selection of good articles on radiosity, and very many abstracts of papers on the subject.
Daylighting Design: http://www.arce.ukans.edu/book/daylight/daylight.htm
A very indepth article about daylight.
|
D3DXMatrixTransformation2D (0) | 2012.11.02 |
---|---|
로컬좌표와 월드 좌표 (0) | 2012.11.02 |
에르미트 (0) | 2012.11.02 |
Z-Fighting (0) | 2012.11.02 |
직교투영 확대 축소 (0) | 2012.11.02 |
unsigned int i;
for ( i = 0; i < mCount-1; ++i )
{
// set up step variables
const float h = 0.1f; // u : 0~1
const float h2 = 0.01f;
const float h3 = 0.001f;
IvVector3 A = 2.0f*mPositions[i]
- 2.0f*mPositions[i+1]
+ mInTangents[i]
+ mOutTangents[i];
IvVector3 B = -3.0f*mPositions[i]
+ 3.0f*mPositions[i+1]
- 2.0f*mInTangents[i]
- mOutTangents[i];
// u *
IvVector3 dP1 = h3*A + h2*B + h*mInTangents[i]; // u = 0.1
//h3*A + h2*B 이것과 다음 것의 차이 만큼만 더해준다
//다음 곡선으로 이동 하기 위한 인자
IvVector3 dP3 = 6.0f * h3*A;
IvVector3 dP2 = dP3 + 2.0f*h2*B;
// 이것이 다음에 더해질 간격이다 6.0f*h3*A + 2.0f*h2*B
// output first point
IvVector3 P = mPositions[i]; // 첫번째 M 의 마지막 성분인 P0을 여기서 더해준다
glVertex3f( P.x, P.y, P.z );
// do forward differencing to next points
for ( u_int j = 1; j <= 11; ++j ) //각 간격당 10개의 성분으로 나누어 표시
{
P += dP1;
glVertex3f( P.x, P.y, P.z );
dP1 += dP2;
dP2 += dP3;
}
}
로컬좌표와 월드 좌표 (0) | 2012.11.02 |
---|---|
라디오시티 (0) | 2012.11.02 |
Z-Fighting (0) | 2012.11.02 |
직교투영 확대 축소 (0) | 2012.11.02 |
폴리곤중간의 텍스처 좌표 구하기 (0) | 2012.11.02 |
Z-Fighting
1. 깊이 버퍼의 크기를 16Bit로 되어 있으면 24Bit로 사용한다.
깊이 버퍼의 대역을 늘려줌으로써, 수용 가능한 좌표계를 늘린다.
D3DParams.AutoDepthStencilFormat = D3DFMT_D24S8;
D3DParams.EnableAutoDepthStencil = TRUE;
2. 좌표계를 작게 사용한다.
위와 같은 맥락으로, Z버퍼는 한정적인데, 무한하게, 맵을 키울경우 Z- Fighting이 발생하기 때문에,
애초에 좌표계를 너무 큰 것을 사용하지 않는 것이 좋다.
쉽게 얘기하면, 맵을 mm로 기준 단위로 나눈다든지.
너무 큰 맵을 사용한다든지,(물론 기준 단위도 크면 상관없지만,)
그러면 나중에 Z들끼리 싸우는 수준이 아니라, 정말 Z와 Fighting하여야 한다.
3. NearZ - FarZ(Z투영 밀어내기)
정상적인 방법이라기 보다는,
사실 Z-Fighting에서 정상적인 방법은 1번,2번까지다. 1번,2번의 한계를 넘으면 발생하는 것이 Z-Fighting이니까..
d3dx9math.h
// Build a perspective projection matrix. (left-handed)
D3DXMATRIX* WINAPI D3DXMatrixPerspectiveFovLH ( D3DXMATRIX *pOut, FLOAT fovy, FLOAT Aspect, FLOAT zn, FLOAT zf );
Perspective 위 함수 단어에서도 알 수 있듯이, 원근이다. 이 원근값은 NearZ - FarZ를 기초로 해서, 행렬을 만들게 된다.
이 투영 행렬을 살짝 변경 시켜서, Z-Fighting을 일으키는 오브젝트를 투영된 화면 속에서 살짝 밀어내는 방법이다.
D3DXMATRIX mProjMat; // 현재 투영 행렬 저장
D3DXMATRIX mZBiasedProjMat; // ZBias 적용된 투영행렬 저장
결국 두 오브젝트는 겹쳐 있으면서도 Z버퍼로 다른 값을 가지게 되기 때문에, Z-Fighting 현상이 해결된다.
이 방식은 모든 그래픽 카드에서 적용되기 때문에, 매우 유용하다.
4. NearZ - FarZ(잘못됐지만, 적당히 넘어가기 좋은 방법)
물론 처음부터 각잡고 개발한 경우에는 이런 경우가 없지만, 항상 예외의 상황은 있는 법.
예를 들면 어떤 모듈 Dll을 사용하고, 그 Dll에서 Rendering을 하는데,
어떤 놈이 만들었는지 모르겠지만, Z-Fighting이 신나게 발생한다. 이미 그 개발자는 사라지고 없다.
내부적으로 Rendering에 어떤 제어를 전혀 할 수 없는 상황이다.(개발자에게는 참 여러가지 상황이 닥치지요.)
이럴 때, 사용하기 좋은 법이라 할 수 있다.
Z-Fighting이 줄어 들 때까지, NearZ를 줄여나간다. 물론 너무 많이 줄이면 중간에 구멍이 생기기 때문에,
한계가 있는 방법이지만,
24bit일 때 FarZ가 10만 이하 정도라면, 티도 안나게 Z파이팅을 해결 될 수도 있다.
이렇게까지 해도, 안 잡히면, 그때는 말로 풀자 -.-.
5. ViewPort
3번과 같은 맥락이다. 다만 이미 만들어진 ViewPort를 약간 변경 시킬 뿐이다.
D3DVIEWPORT9 mViewPort; // 현재 뷰포트 저장
D3DVIEWPORT9 mNViewPort; // 새로운 뷰포트 저장
// 뷰포트 변경을 의해 사용될 값
// 1의 ZBIAS 값을 강제 연산 MinZ - 256/(2^24-1), MaxZ - 256/(2^24-1)
// 2^24 는 설정한 Z버퍼,
//256은 Intel (R) Integrated Graphics 나타낸다. 16의 배수라면 어떤 것이라도 상관없다.
float g_fViewPortBias = 0.0000152588f;
//기존 ViewPort저장
m_pd3dDevice->GetViewport(&mViewport);
// 기존 ViewPort를 mNewViewport에 복사.
mNewViewport = mViewport;
// bias 적용
mNewViewPort.MinZ -= g_fViewportBias;
mNewViewPort.MaxZ -= g_fViewportBias;
// 기존 뷰포트 셋팅
m_pd3dDevice->SetViewport(&mViewport);
후면에 그릴 것
// 새로운 뷰포트 셋팅
m_pd3dDevice->SetViewport(&mNewViewport);
앞면에 그릴 것
// 기본 뷰포트 셋팅
m_pd3dDevice->SetViewport(&mViewport);
6. Depth Bias.
Z-Fighting해결법 중에 가장 형식 있어 보이지만, 그래픽 카드에서 지원을 해줘야 한다. Caps로 먼저 확인하자.
방식은 D3DRS_SLOPESCALEDEPTHBIAS 및 D3DRS_DEPTHBIAS 렌더 옵션을 가지고, 적당히 offset을 주면,
Z-Fighting을 그래픽 카드에서 핸들을 알아서 해준다.
물론 내부적으로 어떻게 움직이는지는 알 수가 없고, 추측만 난무할 뿐이다.
BOOL m_bDepthBiasCap; // 장치가 DepthBias 기능을 가지고 있으면 TRUE
// Depth Bias 가 사용하는 전역 변수
float g_fSlopeScaleDepthBias = 1.0f;
float g_fDepthBias = -0.0005f;
float g_fDefaultDepthBias = 0.0f;
// 새로운 depth bias 기능을 지원하는지 검사
if ((pCaps->RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS) &&
(pCaps->RasterCaps & D3DPRASTERCAPS_DEPTHBIAS))
{
m_bDepthBiasCap = true; // DepthBias 기능이 있다면 TRUE
}
// 빌보드 렌더링됨 ...
// DepthBias 적용됨
if ( m_bDepthBiasCap ) // DepthBias가 지원된다면 TRUE
{
// z-fighting 을 줄이기 위해서 평면상의 프리미티브에
// bias 가 적용될 크기를 결정하기 위해서 사용됨
// bias = (max * D3DRS_SLOPESCALEDEPTHBIAS) + D3DRS_DEPTHBIAS,
// 여기에서 max 는 렌더링되는 삼각형의 최대 깊이 기울기(slope) 이다.
// (역주 : F2DW 는 float 을 DWORD 처럼 읽기 위한 매크로)
m_pd3dDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS,
F2DW(g_fSlopeScaleDepthBias));
m_pd3dDevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW(g_fDepthBias));
}
// 포스터가 렌더링된다...
if ( m_bDepthBiasCap ) // DepthBias 가 지원된다면 TRUE
{
// DepthBias 적용한다.
// 그것을 다시 0(기본값)으로 설정한다.
m_pd3dDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS,
F2DW(g_fDefaultDepthBias));
m_pd3dDevice->SetRenderState(D3DRS_DEPTHBIAS,
F2DW(g_fDefaultDepthBias));
}
[출처] Z-Fighting - Handle|작성자 인생의CORE
라디오시티 (0) | 2012.11.02 |
---|---|
에르미트 (0) | 2012.11.02 |
직교투영 확대 축소 (0) | 2012.11.02 |
폴리곤중간의 텍스처 좌표 구하기 (0) | 2012.11.02 |
[D3DX] Vertex Normal (0) | 2012.11.02 |
D3DXMatrixOrthoLH( &m_ProjectionTM,m_width,m_height,m_zn,m_zf );
이처럼 직교투영을 설정한후
윈도우 사이즈인 width , height 의 비(aspect)에 맞게 m_height, m_width 를 증가시켜준다, 혹은 감소
직교투영 픽킹시에는 윈도우 에 맞게 픽킹이 되려면 화면좌표에서 카메라 좌표까지 변환할때 다음과 같이 처리해주면 된다
D3DXVECTOR3 WindowVector= D3DXVECTOR3(0.0f,0.0f,0.0f);
// 화면좌표에서 투영좌표로
WindowVector.x = (( ((float(Pt.x)*2.0f/width ) - 1.0f)) );
WindowVector.y = (( -((float(Pt.y)*2.0f/height) - 1.0f)) );
* 여기서 화면 사이즈 width, height
-> -1 ~ 1 사이로 변환 한다
카메라까지 지나온점 Pc 라 정의하면 다음과 같이 투영좌표에서 카메라 좌표로 변환된 식을 얻을 수 있다
Pc * 투영행렬 * 화면좌표계행렬 = p` 을
Pc 에 대하여 푼다
이것에 대한 소스는 다음과 같다
// 투영좌표에서 카메라 좌표로 변환
WindowVector.x = (WindowVector.x - ProjectionTM->_14) / ProjectionTM->_11;
WindowVector.y = (WindowVector.y - ProjectionTM->_24) / ProjectionTM->_22;
출처 : http://www.cyworld.com/sjh0628
에르미트 (0) | 2012.11.02 |
---|---|
Z-Fighting (0) | 2012.11.02 |
폴리곤중간의 텍스처 좌표 구하기 (0) | 2012.11.02 |
[D3DX] Vertex Normal (0) | 2012.11.02 |
Directx Aspect 비율 (0) | 2012.11.02 |
http://www.gpgstudy.com/forum/viewtopic.php?p=30027
폴리곤중간의 텍스처 좌표 구하기 |
[?] |
글쓴이 | 메시지 | ||||||
---|---|---|---|---|---|---|---|
psyche04 가입: 2004년 4월 8일 올린 글: 282 소속: 누리엔 소프트웨어 |
| ||||||
moofasa 가입: 2004년 8월 2일 올린 글: 158 소속: 모은행 |
| ||||||
류광 가입: 2001년 7월 25일 올린 글: 3569 소속: GPGstudy 타 사이트 ID(?): docbook.kr::류광, indidev.net::류광 | |||||||
비회원 손님 |
| ||||||
비회원 손님 | |||||||
psyche04 가입: 2004년 4월 8일 올린 글: 282 소속: 누리엔 소프트웨어 | |||||||
jeddli 가입: 2001년 8월 6일 올린 글: 138 소속: NeowizGames |
| ||||||
Gamza 가입: 2001년 10월 11일 올린 글: 610 |
| ||||||
Z-Fighting (0) | 2012.11.02 |
---|---|
직교투영 확대 축소 (0) | 2012.11.02 |
[D3DX] Vertex Normal (0) | 2012.11.02 |
Directx Aspect 비율 (0) | 2012.11.02 |
회전행렬에서 회전축'회전각의 분리(angular displacement) (0) | 2012.11.02 |
라이팅을 계산하는 방법에는 크게 두가지가 있는데
하나는 면(폴리곤) 마다 하나씩 NormalVector(법선벡터) 를 할당해줘서 면단위로 라이팅을 계산하는 방법과
또 하나는 정점(버텍스)마다 하나씩 NormalVector 를 할당해줘서 버텍스 단위로 라이팅을 계산하는 방법이다.
전자를 사용하면 명암이 폴리곤 단위로 입혀지므로 뚝뚝 끊겨있다는 느낌을 받을것이다
반면에 후자를 사용하면 DirectX 내부적으로 버텍스 사이에 보간을 해서(추측임) 라이팅이 부드럽게 나오도록 할 것이다.
CreateTeapt 같은 곡면이 있는 메쉬를 화면에 그려보면 라이팅(Diffuse)이 폴리곤의 경계를 구분할수 없을 정도로 아주 부드럽게 들어가있다는 것을 발견할 수 있다.
그럼 버텍스 마다 법선벡터를 어떻게 정의해야할까?
A라는 버텍스가 있다고할때, 이 A 버텍스를 공유하는 이 폴리곤들 각각의 법선벡터를 구한후 평균값을 버텍스 A의 법선벡터로 정하면 된다.(이 버텍스를 공유하고있는 폴리곤은 1개 일수도있고 2개일수도있고. 100개가 될수도있다.)
그렇다면 버텍스 마다 자신을 공유하고있는 폴리곤들을 어떻게 관리하지.?
버텍스당 매핑되는 polygons 라는 배열변수를 선언해서 폴리곤들의 인덱스값을 넣어놓는다든가 해서 초기에 한번 계산하고 메모리에서 해제해 버리는 방법을 생각하고있다(어차피 법선벡터는 한번 계산되어진후에는 다시 계산될 필요가없으므로)
=====================================================================================================
여기까지가 퍼온글인데 법선벡터가 다시 계산되어질 필요가 없다는건 잘못된 말이다
예를들어 삼각형 모양의 빗변 법선벡터는 스케일이 일어나는경우 법선의 방향이 빗변과 틀려지는 경우가
생기기때문에 법선 계산이 다시 이루어져야 한다
직교투영 확대 축소 (0) | 2012.11.02 |
---|---|
폴리곤중간의 텍스처 좌표 구하기 (0) | 2012.11.02 |
Directx Aspect 비율 (0) | 2012.11.02 |
회전행렬에서 회전축'회전각의 분리(angular displacement) (0) | 2012.11.02 |
d3dx9math.h 함수가 하는 일 설명 (0) | 2012.11.02 |
http://www.gpgstudy.com/forum/viewtopic.php?t=555 [gpg 1권 잘못된 코드 - 4.8 가려진 객체의 제외기법] |
메시지 | |||||||
---|---|---|---|---|---|---|---|
chanick 가입: 2001년 8월 7일 올린 글: 4 소속: Redduck |
| ||||||
류광 가입: 2001년 7월 25일 올린 글: 3569 소속: GPGstudy 타 사이트 ID(?): docbook.kr::류광, indidev.net::류광 |
| ||||||
chanick 가입: 2001년 8월 7일 올린 글: 4 소속: Redduck |
| ||||||
Gamza 가입: 2001년 10월 11일 올린 글: 610 |
|
폴리곤중간의 텍스처 좌표 구하기 (0) | 2012.11.02 |
---|---|
[D3DX] Vertex Normal (0) | 2012.11.02 |
회전행렬에서 회전축'회전각의 분리(angular displacement) (0) | 2012.11.02 |
d3dx9math.h 함수가 하는 일 설명 (0) | 2012.11.02 |
d3dx9math.h 수학적 함수들 수학적 설명 (0) | 2012.11.02 |
http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=30&MAEULNO=12&no=90&page=3
회전행렬에서 회전축'회전각의 분리(angular displacement) | 3D Programming | 2001-03-27 오전 3:46:24 |
이재권 | 번호: 90 / 읽음:3,999 |
안녕하세요.. 이 글은 회전행렬에서 회전축(고유값이 1인 고유벡터)과 그 축을 중심으로 한 회전각을 분리하는것에 대한 글입니다. 전에 쿼터니언을 꺼내 쓰다가 넘 오차가 많이 날때가 있어서 직접 풀어낸 공식입니다. 밤일(??) 하다 지쳐서(???) 여길 쫌 둘러봤는데.. 게임개발게시판은 상대적으로 글이 적네요^^: 그냥 혹시나 해서 올립니다. 일단 아래의 코드는 회전축과 회전각(쿼터니언 아님! 보통 angular displacement라고 도..)으로 행렬을 만드는 공식입니다. 이 공식을 유도하는 방법은 두가지죠. 보통 초보때 배우는 회전축->단위축일치->회전.... 이거하구 다른 하나는 쿼터니언-행렬 변환공식이죠. 둘 다 정리하면 아래의 공식이 나옵니다. D3DMATRIX m; float costheta = cos(theta), sintheta = sin(theta), Length; // must be normalized if not Length = sqrt(x*x + y*y + z*z); x /= Length; y /= Length; z /= Length; m._11 = (x*x) * (1-costheta) + costheta; m._12 = (x*y) * (1-costheta) - (z*sintheta); m._13 = (x*z) * (1-costheta) + (y*sintheta); m._21 = (y*x) * (1-costheta) + (z*sintheta); m._22 = (y*y) * (1-costheta) + costheta ; m._23 = (y*z) * (1-costheta) - (x*sintheta); m._31 = (z*x) * (1-costheta) - (y*sintheta); m._32 = (z*y) * (1-costheta) + (x*sintheta); m._33 = (z*z) * (1-costheta) + costheta; 보시면 알겠지만 행렬에 들어있는 값은 cos(theta/2), sin(theta/2)가 아닌 cos(theta), sin(theta)값이라서 여기서 곧바로 쿼터니언을 꺼낼 경우 대각원소의 절대값으로 기준행을 구한다 하더라도 오차가 쫌 나게 마련이드라구요.. 아래의 코드는 위의 공식을 역으로 풀어 정리한겁니다. 예전에 함 열라 연습장 더럽히면서 풀었던거죠^^: 아무리 찾아봐도 이에 관한 자료는 쿼터니언판밖에 없드라구여TT; void ADfromMAT(D3DMATRIX &m1, D3DVALUE &x, D3DVALUE &y, D3DVALUE &z, D3DVALUE &theta) { D3DVALUE _Trace, _sint, _cost, _cost1; D3DVALUE _11, _12, _13, _21, _22, _23, _31, _32, _33; _11 = m1.m._11; _12 = m1.m._12; _13 = m1.m._13; _21 = m1.m._21; _22 = m1.m._22; _23 = m1.m._23; _31 = m1.m._31; _32 = m1.m._32; _33 = m1.m._33; _Trace = _11 + _22 + _33; if(_Trace == 3.0f) { // Trace = 1 + 2*cosθ 이므로 이 경우 θ가 0이 나온다. x = 1.0f; y = 0.0f; z = 0.0f; theta = 0.0f; return; } _cost = (_Trace - 1.0f)*0.5f; if(_cost <= -1.0f) { _cost = -1.0f; theta = 3.141592654f; } else if(_cost => 1.0f) { _cost = 1.0f; theta = 0.0f; } else { theta = acos(_cost); } #ifdef AD_Fast_Extract // ################################# method 1 if(_Trace+1.0f > 0.0f) { // |theta| < PIE _Trace -= 1.0f; _sint = sin(theta); // theta값이 절대값이므로 결과값은 실제론 |sin (theta)|값이다. x = (_32 - _23) / (_sint*2.0f); y = (_13 - _31) / (_sint*2.0f); z = (_21 - _12) / (_sint*2.0f); } else { // |theta| = PIE if( (_11 > _22) && (_11 > _33)) { // use |x| for calc~ x = sqrt((_11 + 1.0f)*0.5f); _Trace = 1.0f/(2.0f*x); y = _12 * _Trace; z = _13 * _Trace; } else if(_22 > _33) { // use |y| for calc~ y = sqrt((_22 + 1.0f)*0.5f); _Trace = 1.0f/(2.0f*y); x = _12 * _Trace; z = _23 * _Trace; } else { // use |z| for calc~ z = sqrt((_33 + 1.0f)*0.5f); _Trace = 1.0f/(2.0f*z); x = _13 * _Trace; y = _23 * _Trace; } theta = ud_PI; } #else // ############################################### method 2 _cost1 = 1.0f / (1.0f - _cost); if( (_11 > _22) && (_11 > _33)) { // use |x| for calc~ x = sqrt((_11 - _cost)*_cost1); _Trace = 1.0f/(2.0f*(1.0f - _cost)*x); y = (_12 + _21) * _Trace; z = (_13 + _31) * _Trace; _Trace = 1.0f / (2.0f*x); _sint = (_32 - _23)*_Trace; } else if(_22 > _33) { // use |y| for calc~ y = sqrt((_22 - _cost)*_cost1); _Trace = 1.0f/(2.0f*(1.0f - _cost)*y); x = (_12 + _21) * _Trace; z = (_23 + _32) * _Trace; _Trace = 1.0f / (2.0f*y); _sint = (_13 - _31)*_Trace; } else { // use |z| for calc~ z = sqrt((_33 - _cost)*_cost1); _Trace = 1.0f/(2.0f*(1.0f - _cost)*z); x = (_13 + _31) * _Trace; y = (_23 + _32) * _Trace; _Trace = 1.0f / (2.0f*z); _sint = (_21 - _12)*_Trace; } if(_sint < 0.0f) theta = -theta; #endif } 우선 Trace를 이용하면 cosθ값이 간단하게 나온다. 그 다음 단위행렬이 아닐때 회전 각과 회전축을 구하는 방법은 두가지가 있다. ** method 1 theta값을 |theta| 로 고정시키고 x,y,z에 대한 연립방정식으로 푸는 방식이다. 계산속도는 빠르지만 회전각의 절대값이 π에 수렴할수록 회전축의 좌표값에서 오 차가 많이 난다. 회전각은 0 ~ 180도 범위로 나오며 회전축에 회전방향이 반영되어있다. ** method 2 x,y,z,sin(theta)에 대한 연립방정식으로 풀고 sin(theta)의 부호로 theta의 부호 를 결정한다. 이 경우엔 method 1보다 속도는 약간 느리지만 거의 오차가 발생하지 않는다. 회전각은 -180 ~ 180도 범위로 나온다. 공식유도가 잘 안될땐 한가지만 떠올려보세요.. 같은 회전을 의미하는 angular displacement는 항상 2개라는걸..(단위행렬이 아니라면..) 사실 두가지 방법에서 속도차이는 거의 없습니다. 하지만 오차를 보면 method2가 훨씬 효율적이죠. 잘 알려진 보편적인 행렬->쿼터니언 변환공식은 method1과 같은방식입니다. 오차확대조건도 거의 비슷하죠.. 글쎄.. 이게 가장 좋은 방법이라곤 생각되지 않네요^^; 더 좋은 방법을 누군가 알고 계시다면 쫌 알려주시면 고맙겠습니다. 문의는 jacklee666@hotmail.com 으로 해 주시구여^^; 이 게시판에 쓸만한 정보가 많았으면 하네요.. 음.. 실은 요즘 2000용 서버작업중인데^^: 켕.. 여기서 CPIO에 관한 자료를 봐서리.. 넘 고맙구 그냥 가기도 미안해서 이 글을 적습니다. 누구에겐가 조금이라도 도움이 되길 바랍니다. 수고하세요.. | |
[D3DX] Vertex Normal (0) | 2012.11.02 |
---|---|
Directx Aspect 비율 (0) | 2012.11.02 |
d3dx9math.h 함수가 하는 일 설명 (0) | 2012.11.02 |
d3dx9math.h 수학적 함수들 수학적 설명 (0) | 2012.11.02 |
언프로젝션과 픽킹 (0) | 2012.11.02 |
Color관련
D3DXColorAdd - 2 개의 색값을 덧셈 해, 새로운 색값을 생성 한다.
D3DXColorAdjustContrast - 색의 콘트라스트값을 조정한다.
D3DXColorAdjustSaturation - 색의 채도값을 조정한다.
D3DXColorLerp - 선형 보간을 사용해 색값을 생성 한다.
D3DXColorModulate - 2 개의 색을 블렌드 한다.
D3DXColorNegative - 있는 색값에 대한 부의 색값을 생성 한다.
D3DXColorScale - 색값을 스케일링 한다.
D3DXColorSubtract - 2 개의 색값을 뺄셈 해, 새로운 색값을 생성 한다.
기타
D3DXFloat16To32Array - 16 비트 부동 소수점의 배열을 32 비트 부동 소수점으로 변환한다.
D3DXFloat32To16Array - 32 비트 부동 소수점의 배열을 16 비트 부동 소수점으로 변환한다.
D3DXFresnelTerm - 후레넬항을 계산한다.
행렬 관련
D3DXCreateMatrixStack - ID3DXMatrixStack 인터페이스의 인스턴스를 생성 한다.
D3DXMatrixAffineTransformation - 아핀 변환 행렬을 생성 한다.
D3DXMatrixDeterminant - 행렬의 행렬식을 돌려준다.
D3DXMatrixIdentity - 단위행렬을 생성 한다.
D3DXMatrixInverse - 행렬의 역행열을 계산한다.
D3DXMatrixIsIdentity - 행렬이 단위행렬인지 아닌지를 판정한다.
D3DXMatrixLookAtLH - 왼손 좌표계 뷰 행렬을 생성 한다.
D3DXMatrixLookAtRH - 오른손 좌표계 뷰 행렬을 생성 한다.
D3DXMatrixMultiply - 2 개의 행렬의 적을 계산한다.
D3DXMatrixMultiplyTranspose - 2 개의 행렬의 전치적을 계산한다.
D3DXMatrixOrthoLH - 왼손 좌표계 정사영 행렬을 생성 한다.
D3DXMatrixOrthoOffCenterLH - 커스터마이즈 한 왼손 좌표계 정사영 행렬을 생성 한다.
D3DXMatrixOrthoOffCenterRH - 커스터마이즈 한 오른손 좌표계 정사영 행렬을 생성 한다.
D3DXMatrixOrthoRH - 오른손 좌표계 정사영 행렬을 생성 한다.
D3DXMatrixPerspectiveFovLH - 시야에 근거해, 왼손 좌표계 퍼스펙티브 투영 행렬을 생성 한다.
D3DXMatrixPerspectiveFovRH - 시야에 근거해, 오른손 좌표계 퍼스펙티브 투영 행렬을 생성 한다.
D3DXMatrixPerspectiveLH - 왼손 좌표계 퍼스펙티브 투영 행렬을 생성 한다.
D3DXMatrixPerspectiveOffCenterL
D3DXMatrixPerspectiveOffCenterR
D3DXMatrixPerspectiveRH - 오른손 좌표계 퍼스펙티브 투영 행렬을 생성 한다.
D3DXMatrixReflect - 평면에 대해서 좌표계를 반전한 행렬을 생성 한다.
D3DXMatrixRotationAxis - 임의의 축을 회전축으로 해 회전하는 행렬을 생성 한다.
D3DXMatrixRotationQuaternion - 쿼터니온으로부터 회전 행렬을 생성 한다.
D3DXMatrixRotationX - x 축을 회전축으로 해 회전하는 행렬을 생성 한다.
D3DXMatrixRotationY - y 축을 회전축으로 해 회전하는 행렬을 생성 한다.
D3DXMatrixRotationYawPitchRoll - 요, 피치, 및 롤을 지정해 행렬을 생성 한다.
D3DXMatrixRotationZ - z 축을 회전축으로 해 회전하는 행렬을 생성 한다.
D3DXMatrixScaling - x 축, y 축, z 축으로 따라 스케일링 하는 행렬을 생성 한다.
D3DXMatrixShadow - 지오메트리를 평면에 투영 하는 행렬을 생성 한다.
D3DXMatrixTransformation - 변환 행렬을 생성 한다.
D3DXMatrixTranslation - 오프셋(offset)를 지정해 행렬을 생성 한다.
D3DXMatrixTranspose - 행렬의 전치행렬을 돌려준다.
평명 관련
D3DXPlaneDot - 평면과 4D 벡터의 내적을 계산한다.
D3DXPlaneDotCoord - 평면과 3D 벡터의 내적을 계산한다. 벡터의 w 파라미터는, 1 이다고 보여진다.
D3DXPlaneDotNormal - 평면과 3D 벡터의 내적을 계산한다. 벡터의 w 파라미터는, 0 이다고 보여진다.
D3DXPlaneFromPointNormal - 점트와 법선으로부터 평면을 생성 한다.
D3DXPlaneFromPoints - 3 개의 점으로부터 평면을 생성 한다.
D3DXPlaneIntersectLine - 평면과 직선의 교점을 조사한다.
D3DXPlaneNormalize - 평면의 법선의 길이를 정규화하기 위해(때문에), 평면의 계수를 정규화한다.
D3DXPlaneTransform - 행렬을 사용해 평면을 변환 한다. 입력 행렬은, 실제의 변환의 역전치행렬이다.
D3DXPlaneTransformArray - 행렬을 사용해 평면을 변환 한다. 입력 행렬은, 실제의 변환의 역전치행렬이다.
쿼터니온 관련
D3DXQuaternionBaryCentric - 중심 좌표의 쿼터니온을 돌려준다.
D3DXQuaternionConjugate - 쿼터니온의 공역을 돌려준다.
D3DXQuaternionDot - 2 개의 쿼터니온의 내적을 돌려준다.
D3DXQuaternionExp - 지수함수를 계산한다.
D3DXQuaternionIdentity - 항등 쿼터니온을 돌려준다.
D3DXQuaternionInverse - 쿼터니온을 모두 부려, 재정규화한다.
D3DXQuaternionIsIdentity - 쿼터니온이 항등 쿼터니온일지 어떨지를 판정한다.
D3DXQuaternionLength - 쿼터니온의 길이를 돌려준다.
D3DXQuaternionLengthSq - 쿼터니온의 길이의 2 승을 돌려준다.
D3DXQuaternionLn - 자연대수를 계산한다.
D3DXQuaternionMultiply - 2 개의 쿼터니온의 적을 계산한다.
D3DXQuaternionNormalize - 정규화한 길이의 쿼터니온을 계산한다.
D3DXQuaternionRotationAxis - 임의의 축을 회전축으로서 쿼터니온을 회전시킨다.
D3DXQuaternionRotationMatrix - 회전 행렬로부터 쿼터니온을 생성 한다.
D3DXQuaternionRotationYawPitchR
D3DXQuaternionSlerp - 구면선형 보간을 사용해, 2 개의 쿼터니온간을 보간 한다.
D3DXQuaternionSquad - 구면2차 보간을 사용해, 쿼터니온간을 보간 한다.
D3DXQuaternionSquadSetup - 구면2차 보간의 제어 포인트를 설정한다.
D3DXQuaternionToAxisAngle - 쿼터니온의 회전축과 회전 각도를 계산한다.
벡터 관련
<2D 벡터>
D3DXVec2Add - 2 개의 2D 벡터를 추가한다.
D3DXVec2BaryCentric - 지정한 2D 벡터를 사용해, 중심 좌표의 점을 돌려준다.
D3DXVec2CatmullRom - 지정된 2D 벡터를 사용해, Catmull-Rom 보간을 실행한다.
D3DXVec2CCW - 2 개의 2D 벡터의 외적을 계산해, z 요소를 돌려준다.
D3DXVec2Dot - 2 개의 2D 벡터의 내적을 계산한다.
D3DXVec2Hermite - 지정된 2D 벡터를 사용해, 에르미트의 스플라인 보간을 실행한다.
D3DXVec2Length - 2D 벡터의 길이를 돌려준다.
D3DXVec2LengthSq - 2D 벡터의 길이의 2 승을 돌려준다.
D3DXVec2Lerp - 2 개의 2D 벡터간의 선형 보간을 실행한다.
D3DXVec2Maximize - 2 개의 2D 벡터의 최대치로 구성되는 2D 벡터를 돌려준다.
D3DXVec2Minimize - 2 개의 2D 벡터의 최소 값으로 구성되는 2D 벡터를 돌려준다.
D3DXVec2Normalize - 2D 벡터의 정규화한 벡터를 돌려준다.
D3DXVec2Scale - 2D 벡터를 스케일링 한다.
D3DXVec2Subtract - 2 개의 2D 벡터를 뺄셈 한다.
D3DXVec2Transform - 지정된 행렬에 의해 2D 벡터를 변환 한다.
D3DXVec2TransformArray - 지정된 행렬에 의해 2D 벡터의 배열을 변환 한다.
D3DXVec2TransformCoord - 지정된 행렬에 의해 2D 벡터를 변환 해, 그 결과를 w = 1 에 투영 한다.
D3DXVec2TransformCoordArray - 지정된 행렬에 의해 배열 (x, y, 0, 1)을 변환 해, 그 결과를 w = 1 에 투영 한다.
D3DXVec2TransformNormal - 지정된 행렬에 의해 2D 벡터 법선을 변환 한다.
D3DXVec2TransformNormalArray - 지정된 행렬에 의해 배열 (x, y, 0, 0)을 변환 한다.
<3D 벡터>
D3DXVec3Add - 2 개의 3D 벡터를 추가한다.
D3DXVec3BaryCentric - 지정한 3D 벡터를 사용해, 중심 좌표의 점을 돌려준다.
D3DXVec3CatmullRom - 지정된 3D 벡터를 사용해, Catmull-Rom 보간을 실행한다.
D3DXVec3Cross - 2 개의 3D 벡터의 외적을 계산한다.
D3DXVec3Dot - 2 개의 3D 벡터의 내적을 계산한다.
D3DXVec3Hermite - 지정된 3D 벡터를 사용해, 에르미트의 스플라인 보간을 실행한다.
D3DXVec3Length - 3D 벡터의 길이를 돌려준다.
D3DXVec3LengthSq - 3D 벡터의 길이의 2 승을 돌려준다.
D3DXVec3Lerp - 2 개의 3D 벡터간의 선형 보간을 실행한다.
D3DXVec3Maximize - 2 개의 3D 벡터의 최대치로 구성되는 3D 벡터를 돌려준다.
D3DXVec3Minimize - 2 개의 3D 벡터의 최소 값으로 구성되는 3D 벡터를 돌려준다.
D3DXVec3Normalize - 3D 벡터의 정규화한 벡터를 돌려준다.
D3DXVec3Project - 개체 공간으로부터 스크린 공간에 벡터를 투영 한다.
D3DXVec3ProjectArray - 개체 공간으로부터 스크린 공간에 벡터를 투영 한다.
D3DXVec3Scale - 3D 벡터를 스케일링 한다.
D3DXVec3Subtract - 2 개의 3D 벡터를 뺄셈 한다.
D3DXVec3Transform - 지정된 행렬에 의해 벡터 (x, y, z, 1)를 변환 한다.
D3DXVec3TransformArray - 지정된 행렬에 의해 배열 (x, y, z, 1)을 변환 한다.
D3DXVec3TransformCoord - 지정된 행렬에 의해 3D 벡터를 변환 해, 그 결과를 w = 1 에 투영 한다.
D3DXVec3TransformCoordArray - 지정된 행렬에 의해 배열 (x, y, z, 1)을 변환 해, 그 결과를 w = 1 에 투영 한다.
D3DXVec3TransformNormal - 지정된 행렬에 의해 3D 벡터 법선을 변환 한다.
D3DXVec3TransformNormalArray - 지정된 행렬에 의해 배열 (x, y, z, 0)을 변환 한다.
D3DXVec3Unproject - 스크린 공간으로부터 개체 공간에 벡터를 투영 한다.
D3DXVec3UnprojectArray - 스크린 공간으로부터 개체 공간에 벡터를 투영 한다.
<4D 벡터>
D3DXVec4Add - 2 개의 4D 벡터를 추가한다.
D3DXVec4BaryCentric - 지정한 4D 벡터를 사용해, 중심 좌표의 점을 돌려준다.
D3DXVec4CatmullRom - 지정된 4D 벡터를 사용해, Catmull-Rom 보간을 실행한다.
D3DXVec4Cross - 4 차원 벡터의 외적을 산출한다.
D3DXVec4Dot - 2 개의 4D 벡터의 내적을 요구한다.
D3DXVec4Hermite - 지정된 4D 벡터를 사용해, 에르미트의 스플라인 보간을 실행한다.
D3DXVec4Length - 4D 벡터의 길이를 돌려준다.
D3DXVec4LengthSq - 4D 벡터의 길이의 2 승을 돌려준다.
D3DXVec4Lerp - 2 개의 4D 벡터간의 선형 보간을 실행한다.
D3DXVec4Maximize - 2 개의 4D 벡터의 최대치로 구성되는 4D 벡터를 돌려준다.
D3DXVec4Minimize - 2 개의 4D 벡터의 최소 값으로 구성되는 4D 벡터를 돌려준다.
D3DXVec4Normalize - 4D 벡터의 정규화한 벡터를 돌려준다.
D3DXVec4Scale - 4D 벡터를 스케일링 한다.
D3DXVec4Subtract - 2 개의 4D 벡터를 뺄셈 한다.
D3DXVec4Transform - 지정된 행렬에 의해 4D 벡터를 변환 한다.
D3DXVec4TransformArray - 행렬을 지정해 벡터의 배열을 변환 한다.
Directx Aspect 비율 (0) | 2012.11.02 |
---|---|
회전행렬에서 회전축'회전각의 분리(angular displacement) (0) | 2012.11.02 |
d3dx9math.h 수학적 함수들 수학적 설명 (0) | 2012.11.02 |
언프로젝션과 픽킹 (0) | 2012.11.02 |
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
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
회전행렬에서 회전축'회전각의 분리(angular displacement) (0) | 2012.11.02 |
---|---|
d3dx9math.h 함수가 하는 일 설명 (0) | 2012.11.02 |
언프로젝션과 픽킹 (0) | 2012.11.02 |
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
** 화면좌표에서 공간좌표로의 변환 **
copyrightⓒ Gamza
BLACK&WHITE 같은 마우스를 만들려면 마우스의 2D위치를 3D공간좌표로 변환하는것이 필요해집니다. 등장하는 '신의손'이 실제 공간좌표를 갖기때문이죠. 이것은 상당히 효과만점인 반면, 물체를 Pick할줄만 알면 아주 간단하게 끝나는 아주 쉬운 기법입니다.
D3D의 Pick예제를 보면 물체를 picking하는 기법이 깔끔하게 나와있으니 그걸 그대로 사용하도록 하겠습니다.
2D마우스 좌표를 3D좌표로 변환하는 순서는 다음과 같습니다.
(1) 마우스 좌표를 가지고 공간상에 반직선을 만든다. (언프로젝션)
(2) 반직선과 교차하는 폴리곤중 가장 가까운것을 택한다. ( picking )
(3) 직선과 폴리곤의 교점을 구한다.( == 3D마우스 좌표 )
1. 언프로젝션
우선 마우스 좌표에 해당하는 3D점을 하나 구합니다.
( 아래식이 이해가 안되시는 분들은 '언프로젝션 ( Unprojection )' 과 그것에 링크된문서들을 꼭 읽어보세요. 안읽어보셔도 당장 써먹는데야 지장이 없겠지만....배워서 남주는거 아니니깐요.)
m_pd3dDevice->GetViewport(&vp); m_pd3dDevice->GetTransform( D3DTS_PROJECTION, &matProj ); GetCursorPos( &ptCursor ); ScreenToClient( m_hWnd, &ptCursor ); v.x = (( (((ptCursor.x-vp.X)*2.0f/vp.Width ) - 1.0f)) - matProjection._31 ) / matProjection._11; v.y = ((- (((ptCursor.y-vp.Y)*2.0f/vp.Height) - 1.0f)) - matProjection._32 ) / matProjection._22; v.z = 1.0f;
언프로젝션하는 부분을 주의하세요. D3D예제에 있는대로 하시면 안될경우가 있습니다.
( 투영행렬에 클리핑행렬이 포함된것 같은경우 )
...
이제 카메라 좌표와 구해진 점을 잇는 반직선을 만들면 되죠,
m_pd3dDevice->GetTransform( D3DTS_VIEW, &matView ); D3DXMatrixInverse( &m, NULL, &matView ); vPickRayDir.x = v.x*m._11 + v.y*m._21 + v.z*m._31; vPickRayDir.y = v.x*m._12 + v.y*m._22 + v.z*m._32; vPickRayDir.z = v.x*m._13 + v.y*m._23 + v.z*m._33; vPickRayOrig.x = m._41; vPickRayOrig.y = m._42; vPickRayOrig.z = m._43;
2. Picking
D3D예제를 보시면 IntersectTriangle 함수를 사용해서 위에서 구한 반직선이 삼각형에 교차하는지를 검사해서 Pick를 수행하게 됩니다.
근디 우리가 해야할건...그중에서 카메라에 젤 가까운넘을 골라내는겁니다.
거럼 ...어떤넘이 카메라에 가장 가까운 넘인가???
BOOL CMyD3DApplication::IntersectTriangle( const D3DXVECTOR3& orig, const D3DXVECTOR3& dir, D3DXVECTOR3& v0, D3DXVECTOR3& v1, D3DXVECTOR3& v2, FLOAT* t, FLOAT* u, FLOAT* v )
IntersectTriangle함수의 6번째 인자(t) 는 출력용 인자입니다. 폴리곤과 반직선의 교점이 카메라에서 얼마나 떨어져 있는지를 알려주죠.
다시말하면....교차가 발견될때마다 출력된 t 값을 조사해서...젤 작은넘을 유지하도록 하면 됩니다.
t가 제일 작은 폴리곤이 실제로 picking된 폴리곤이죠.
3. 3D마우스 좌표계산
최종적인 3D좌표는 반직선과 IntersectTriangle함수의 출력으로 얻어진 t값으로부터 다음과 같이 구해집니다.
자...끝났습니다.
참....마우스가 화면상의 어떤것도 선택하고 있지 않을경우를 별도 처리 해야한다는 것을 잊지마세요.
아무것도 선택되지 않았다면 '카메라로부터의 기본거리'를 적용하는 것이 가장 자연스러울듯 합니다.
이렇게 처리하면 되죠.
이거...응용할데가 많겠죠?
레벨편집기라던가, RPG, 전략시뮬같은데서도 유용하게 쓰일듯...
d3dx9math.h 함수가 하는 일 설명 (0) | 2012.11.02 |
---|---|
d3dx9math.h 수학적 함수들 수학적 설명 (0) | 2012.11.02 |
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이에 대해서
간단히 말씀드리도록 하겠습니다.
단순하게 말하자면 D3DXVec3TransformNormal 은 벡터를 변환하는 것이고
D3DXVec3TransformCoord 는 위치를 변환하는 것입니다.
벡터와 행렬을 곱하기 위해서는 행과 열이 같은 차수여야 한다는 것을
알고 계실 것입니다. 예를 들어서
[x, y, z] 라는 3차원 벡터가 존재하는데 D3D 에서는 4x4 행렬을 곱하게 됩니다.
이러한 벡터를 행렬과 연산을 할 때는 차수를 맞춰줘야지 곱할 수 있게 됩니다.
즉 3차 벡터를 마치 1x4 행렬 혹은 4x1 행렬인 것처럼 만들어 주어야
4x4 행렬과 곱하는 것이 가능하다는 것이죠.
D3DXVec3TransformNormal 은 [x, y, z] 를 [x, y, z, 0] 으로 만들고
D3DXVec3TransformCoord 는 [x, y, z] 를 [x, y, z, 1] 로 만들어서 연산을 합니다.
물론 결과는 [Rx, Ry, Rz] 형태인 3차원 벡터로 반환하게 되죠.
그런데 마지막에 0 이 붙느냐 1 이 붙느냐가 어떤 의미인지 궁금하실 것 같습니다.
Direct3D 에서 사용하는 행렬의 마지막 열 성분은 위치 변환(Translation)을 의미하고 있습니다.
즉 다음과 같은 형태를 띠고 있습니다.
| _11 _12 _13 Tx |
| _21 _22 _23 Ty |
| _31 _32 _33 Tz |
| _41 _42 _43 1 |
여기에서 _xx 식으로 표기한 부분은 회전과 크기 변환에 대한 부분입니다.
이것을 D3DXVec3TransformNormal 을 위해 만들어낸 행렬과 연산을 해 봅시다.
| _11 _12 _13 Tx | | x |
| _21 _22 _23 Ty | | y |
| _31 _32 _33 Tz | | z |
| _41 _42 _43 1 | | 0 |
이것을 연산할 때 Tx, Ty, Tz 요소는 0 과 곱해지게 된다는 것을 알 수 있습니다. 즉 다시 말해 변환 행렬의 이동 성분이 반영되지 않는다는 것입니다.
다음으로 D3DXVec3TransformCoord 를 위해 만들어낸 행렬과 연산을 해 봅시다.
| _11 _12 _13 Tx | | x |
| _21 _22 _23 Ty | | y |
| _31 _32 _33 Tz | | z |
| _41 _42 _43 1 | | 1 |
Tx, Ty, Tz 요소들은 1과 곱해지기 때문에 변환 행렬의 이동 성분이 반영되고 있음을 알 수 있습니다.
d3dx9math.h 수학적 함수들 수학적 설명 (0) | 2012.11.02 |
---|---|
언프로젝션과 픽킹 (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기. (0) | 2012.10.27 |
D3DXVec3TransformCoord은 뭔가요? |
[?] |
글쓴이 | 메시지 | ||||||
---|---|---|---|---|---|---|---|
imays 가입: 2003년 2월 18일 올린 글: 1043 |
| ||||||
maxidea 손님 |
| ||||||
maxidea 손님 |
| ||||||
imays 가입: 2003년 2월 18일 올린 글: 1043 |
| ||||||
zizisky 가입: 2002년 8월 28일 올린 글: 11 소속: Netmarble |
| ||||||
maxidea 손님 |
| ||||||
언프로젝션과 픽킹 (0) | 2012.11.02 |
---|---|
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기. (0) | 2012.10.27 |
convex polygon 이든 concave polygon 이든 모든 다각형의 내부에서 점의 존재여부 판단 (0) | 2012.10.27 |
GPG 사이트 에서..
D3DXVec3TransformNormal 과
D3DXVec3TransformCoord 의 차이에 대해서
간단히
말씀드리도록 하겠습니다.
단순하게 말하자면 D3DXVec3TransformNormal 은 벡터를 변환하는 것이고
D3DXVec3TransformCoord 는 위치를 변환하는 것입니다.
벡터와
행렬을 곱하기 위해서는 행과 열이 같은 차수여야 한다는 것을
알고 계실 것입니다. 예를 들어서
[x, y, z]
라는 3차원 벡터가 존재하는데 D3D 에서는 4x4 행렬을 곱하게 됩니다.
이러한 벡터를 행렬과 연산을 할 때는 차수를 맞춰줘야지
곱할 수 있게 됩니다.
즉 3차 벡터를 마치 1x4 행렬 혹은 4x1 행렬인 것처럼 만들어 주어야
4x4 행렬과
곱하는 것이 가능하다는 것이죠.
D3DXVec3TransformNormal
은 [x, y, z] 를 [x, y, z, 0] 으로 만들고
D3DXVec3TransformCoord 는 [x, y, z] 를 [x, y, z, 1] 로
만들어서 연산을 합니다.
물론 결과는 [Rx, Ry, Rz] 형태인 3차원 벡터로 반환하게 되죠.
그런데 마지막에
0 이 붙느냐 1 이 붙느냐가 어떤 의미인지 궁금하실 것 같습니다.
Direct3D 에서 사용하는 행렬의 마지막 열 성분은 위치
변환(Translation)을 의미하고 있습니다.
즉 다음과 같은 형태를 띠고 있습니다.
| _11 _12 _13
Tx |
| _21 _22 _23 Ty |
| _31 _32 _33 Tz |
| _41 _42 _43 1 |
여기에서 _xx 식으로 표기한 부분은 회전과 크기 변환에 대한 부분입니다.
이것을 D3DXVec3TransformNormal 을 위해 만들어낸 행렬과 연산을 해 봅시다.
| _11 _12 _13 Tx | | x |
| _21 _22 _23 Ty | | y |
| _31 _32 _33
Tz | | z |
| _41 _42 _43 1 | | 0 |
이것을 연산할 때 Tx, Ty, Tz 요소는 0 과 곱해지게
된다는 것을 알 수 있습니다. 즉 다시 말해 변환 행렬의 이동 성분이 반영되지 않는다는 것입니다.
다음으로 D3DXVec3TransformCoord 를 위해 만들어낸 행렬과 연산을 해 봅시다.
| _11 _12 _13 Tx | | x |
| _21 _22 _23 Ty | | y |
| _31 _32 _33
Tz | | z |
| _41 _42 _43 1 | | 1 |
Tx, Ty, Tz 요소들은 1과 곱해지기 때문에 변환
행렬의 이동 성분이 반영되고 있음을 알 수 있습니다.
------------------------------------------------------------------
이제 질문하신 부분에 대해서 이야기해 보도록 하겠습니다.
D3DXVECTOR3 vA, vB;
D3DXMATRIX mTransform;
vB = D3DXVECTOR3(1, 1, 1);
D3DXMatrixIdentity(&mTransform);
D3DXVec3TransformNormal(&vA, &vB,
&mTransform);
이라는 식으로 했을 때 이것이 어떠한 결과를 낳으며
행렬의 역할은 무엇인가에 대해서
질문하신 것 같습니다.
이 함수는 기본적으로 위에서 설명했듯이 vB 가 위치를 나타내는 점이 아니라
방향과 크기를
나타내는 벡터라는 가정을 하고 있습니다.
또한 변환 행렬의 이동 성분은 무시하기 때문에 위의 함수가 최종적으로 반환하는 vA 는
로컬 공간에서의 벡터에 회전 및 스케일 변환을 적용했을 때의 월드 공간에서의 벡터를 의미하게 됩니다.
이러한 연산을
사용하는 것은 로컬에서의 법선 벡터가 변환 이후
월드에서 어떤 방향과 크기를 가지는 지를 알아내야 하는 경우입니다.
하지만 만약 이것이 점이라 가정하고 로컬 공간에서 mTransform 을 적용한 것 만큼
월드 공간으로 이동시키고자
한다면
D3DXVec3TransformNormal 이 아니라 D3DXVec3TransformCoord 를 사용해 줘야 합니다.
그래야지 이동
성분이 평가가 되어서 월드 공간에서의 위치를 얻을 수 있겠죠.
답변이 제대로 되었는지 모르겠지만... 도움이 되었으면 좋겠네요.
즐플하세요.
언프로젝션과 픽킹 (0) | 2012.11.02 |
---|---|
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기. (0) | 2012.10.27 |
convex polygon 이든 concave polygon 이든 모든 다각형의 내부에서 점의 존재여부 판단 (0) | 2012.10.27 |
http://wibler.egloos.com/3407369
제목 그대로 그림에서처럼 두 벡터 사이의 각도를 구해보죠.
이건 원운동을 하기
위해 필요한 작업이에요.
사실 이건 고등학교 수학책에도 나올만한 문제이지요. 백터의 내적을 이용하면 되거든요.
a점의
x1, y1이고 b점이 x2, y2면 아시다시피 두 백터의 내적은 다음과 같아요.
a*b = |a||b|*cos(c) -- 앞의 *
는 내적을 표해요. 표현하기가 마땅치 않군요.
이걸 풀어쓰고 정리하면(루트는 sqrt()로 쓸게요)
cos(c) = {
x1 * y1 + x2 * y2 } / { sqrt( x1^2 + y1^2 ) * sqrt( x2^2 + y2^2 ) }
이제
cos의 역함수인 acos()함수를 쓰면 c를 구할 수 있어요. 보통은.
그런데 여기서 math.h 에 들어있는 acos의 한계가
나와요. 이놈은 리턴을 0~180까지의 범위만으로 하거든요.
다시 설명하자면 다음과 같이 점이 있을때.
나는 a에서 b까지 시계방향으로 측정한 각도를 구하고 싶은데 위 방법대로
하면
반시계방향의 각도가 나와 버린다는 소리죠.
그래서 내적을 이용해 구하기전에 약간의 사전작업을 해야 해요. 다시
그림을 보자면.
일단 a를 90도 회전시킨 a' 을 구해요. 이건 매우 쉬워요.
주의해야할껀
좌표계의 방향이에요. 보통 윈도우를 만들면 아래쪽으로 갈수록
y값이 커지는건 알고 있죠. 그걸 기준으로 설명할게요.
a
가 x1, y1 이고 a' 이 x1', y1' 일때 시계방향으로 회전시키고 싶으면
x1' = -y1, y1' = x1이
되요.
반시계방향이라면. x1' = y1, y1' = -x1이 되죠. 조금만 생각해보면 답나와요.
이제 a'점과
b점사이의 각도를 위의 방식으로 내적을 이용해서 구해요.
이제 거의 다 됬어요. 방금구한 각도를 c' 라고
할때.
방금구한 각도가 90 보다 크면 a에서 b까지의 각도는
180도 보다 크다는 이야기에요.
그렇다면 간단하죠.
c'이 90보다 작을때에는 a와 b사이의 각도는
내적을 이용해서 바로 구할 수 있고.
c'이 90보다 클 때에는 360에서 내적을 이용해 구한 a와 b사이의 각도를 빼면
되지요.
아까 설명했다시피 c'이 90보다 클때는 a와 b사이의 각도가 반시계방향의 각도로 나오잖아요.
0~180의
범위로.
이런식으로 시계방향 혹은 반시계방향으로 0~360범위의 각도를 구할 수 있답니다.
아래 코드는 그걸
짜본거에요.
세번째 인자인 isClockWise는 true로 하면 시계방향, false로 하면 반시계방향의 각도를
구합니다.
fPOINT 는 전에 말했다시피 float x, y로 구성된
구조체입니다.
----------------------------------------------------------------------------------------
float
CPath::CalAngleBetweenTwoPoint(fPOINT ptPoint1, fPOINT ptPoint2, BOOL
isClockWise)
{
//acos()나 asin()함수로 구하는 각도를 degree로 나타냈을때
//그 범위는
0~180이다.
//각도를 0~360으로 나타내기 위한 사전작업으로
//기준점(ptPoint1)을 90도 회전시킨 점을
구한다.
fPOINT ptRotated90Point1 = {0,0};
if(isClockWise)
{
ptRotated90Point1.x=
-ptPoint1.y;
ptRotated90Point1.y =
ptPoint1.x;
}
else
{
ptRotated90Point1.x =
ptPoint1.y;
ptRotated90Point1.y = -ptPoint1.x;
}
//앞서 계산한 점과 두번째 인자로 받은 점간의 각도를 구함.
//이 각도가 90도보다 크다면 첫번째인자로 받은 기준점과 두번째
점간의 각도가
//180도보다 크다는 것을 의미한다.
float fAng = acos( (ptRotated90Point1.x * ptPoint2.x + ptRotated90Point1.y *
ptPoint2.y) /
(sqrt(
ptRotated90Point1.x * ptRotated90Point1.x + ptRotated90Point1.y *
ptRotated90Point1.y ) *
sqrt(ptPoint2.x * ptPoint2.x +
ptPoint2.y * ptPoint2.y) ) )
* 360 / TWOPI;
//fAng의 크기에 따라 두점사이의 각도를 다른 방식으로 구함.
if(fAng > 90)
return 360 -
acos( (ptPoint1.x * ptPoint2.x + ptPoint1.y * ptPoint2.y) /
(sqrt( ptPoint1.x * ptPoint1.x +
ptPoint1.y * ptPoint1.y ) *
sqrt(ptPoint2.x * ptPoint2.x +
ptPoint2.y * ptPoint2.y) ) )
* 360 / TWOPI;
else
return acos( (ptPoint1.x * ptPoint2.x + ptPoint1.y * ptPoint2.y)
/
(sqrt( ptPoint1.x * ptPoint1.x
+ ptPoint1.y * ptPoint1.y ) *
sqrt(ptPoint2.x * ptPoint2.x +
ptPoint2.y * ptPoint2.y) ) )
* 360 / TWOPI;
}
언프로젝션과 픽킹 (0) | 2012.11.02 |
---|---|
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
convex polygon 이든 concave polygon 이든 모든 다각형의 내부에서 점의 존재여부 판단 (0) | 2012.10.27 |
http://lsujang.egloos.com/845586
벡터의
외적을 이용하면 3개의 점(A,
B, C)이
이루는 삼각형 안에 또다른 점 D가
포함되어 있는지의 여부를 손쉽게 판단할 수 있다. 바로 곱하여지는 두 벡터의 위치에 따라 법선 벡터의 방향이 달라진다는 성질을 이용한 것이다.
여기선 오른손 좌표계를 이용한다고 정하자.(머 왼손 좌표계라도 상관은 없다.)
언프로젝션과 픽킹 (0) | 2012.11.02 |
---|---|
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기. (0) | 2012.10.27 |