반응형

회전변환에 올렸던 내용중...

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' 이 된다

반응형

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

동차좌표  (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

 

 

반응형

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

원근투형 TM  (0) 2012.11.02
회전행렬  (0) 2012.11.02
일차 독립, 일차 종속  (0) 2012.11.02
3D 벡터 기초 연산  (0) 2012.11.02
왼손 좌표계 vs 오른손 좌표계 의 외적  (0) 2012.11.02
반응형

 

 

일차독립 : 식을 만족하려면 계수들이 0이 되야한다

일차종속 : 식을 만족하기위해서는 계수들이 0 이 아닐 수도 있다

반응형

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

회전행렬  (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 입니다.

 

 변위를 다룰때는 방향을 고려하는 것이 매우 중요합니다. 스칼라 양인 거리와 벡터량인 변위는 매우 큰 차이가 있기 때문입니다.  변위를 계산할때 고려해야 할 것은 시작점과 끝점의 위치뿐이고 그 사이에 일어나는 일은 그다지 중요하지 않습니다.

 풋볼경기를 예로 들어보면 다음과 같습니다.

 


[풋볼 선수의 변위]
 
 볼 경기에서 한 선수가 20야드 라인에서 공을 잡고 달리기 시작합니다. 도중에 수비수들을 피해 요리조리 빠져나가며 계속 달려나가다가 50 야드 라인에서 태클을 당했다면 이 선수의 전진 거리로 인정되는건 A라인이 아니라 B 라인이 됩니다. 벡터도 이와 마찬가지입니다. 도중에 지그재그로 가던지 곧바로 가던지 결국 중요한것은 출발점과 도착점 뿐이라는 것이죠.
 
 위의 그림에서 A 값은 스칼라 값이고 B 값은 벡터 값이 되는 것입니다.
=================================================
|      변위
|      ΔX = X2 - X2
================================================= 
 
 자자 그럼 문제 나갑니다.  슈퍼마리오 게임을 하고 있는데 수평위치 200픽셀에서 시작햇습니다. 오른쪽으로 마구마구 이동하다가 250픽셀쯤 왔을때 버섯을 놓친 것을 깨닫고 다시 100픽셀에 있는 버섯을 먹고 왔습니다. 그 후 마리오는 다시 데이지 공주가있는 450 픽셀까지 달려가 공주를 만났다면 이 마리오의 최종 변위는 몇일까요? 또 실제 이동거리는 얼마일까요?
 
 최종변위부터 계산할까요? 시작점이 200픽셀이고 도착점이 450픽셀이니 450 - 200 =  250 간단하죠?
실제 이동거리도 간단합니다. 처음 200픽셀에서 250픽셀까지 갔을때 50픽셀을 이동했고 다시 버섯이 있는 100픽셀까지 이동했으니 150 픽셀을 더 가서 현재까지 200픽셀을 이동했습니다. 그 후에 공주가 있는 450픽셀까지 이동했으니 추가로 350 픽셀을 더 이동했네요. 그래서 실제 이동거리는 550픽셀이 되는 것입니다.
 
 
 
 
 2. 벡터의 기술 방법
 
 앞에서 우리는 1차원 벡터의 방향을 기술할때 양수와 음수만으로 충분하다는 것을 알았습니다.
하지만 2차원과 3차원에서는 이것만으로는 충분하지 않습니다.
 2차원에서 벡터를 기술하는 방법은 2가지가 있습니다. 그것은 바로 극좌표와 데카르트 좌표계입니다. 먼저 극좌표계부터 살펴보겠습니다.
[극좌표로 표현한 벡터 A]
 
극좌표는 벡터가 실제로 어떤 모양인지를 파악하는 가장 쉬운 방법입니다.
===================================================================
|   벡터 A 의 크기를  ||A|| 라 하고, 벡터의 방향을 θ라 할때
|   벡터 A = ||A|| @ θ
===================================================================
 
 
 
데카르트 좌표는 조금 덜 직관적이지만, 벡터를 코딩할때 사용하는 형식입니다.

극좌표와 같이 길이와 방향으로 벡터를 기술하는 대신, 벡터를 수평과 수직 변위로 나타낼 수도 있으며 이것은 데카르트 좌표계의 방식과 매우 유사합니다. 데카르트 좌표를 구성하는 두가지 요소는 수평과 수직성분이며 이렇게 표현한 형식을 i j  형식이라고 합니다.

 

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

|   데카르트 좌표 (벡터 성분)

|   벡터의 x 축 방향의 단위벡터를  i^ ,  y 축 방향의 단위벡터를 j^ 라고하면,

|   벡터 B = b1 i^  + b2 j^

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

** 단위벡터를 나타내는 문자가 원래는 i 위에 ^ 이 모자처럼 씌어진 그림입니다만 키보드에서 적절한 문자를 찾을 수 없어서 뒤에다가 썻으니 이점을 숙지하여 다른 책이나 자료를 볼때 혼동하지 마시길바랍니다. 아래 그림을 참고하시기 바랍니다.

 

[데카르트 좌표로 표현된 벡터 B]

 

 

컴퓨터의 화면은 격자 형태로 설정되습니다. 그렇기 때문에 코딩을 할때 데카르트 좌표를 사용합니다.

혹 극좌표를 사용하려고 한다고 해도 결국은 데카르트 좌표로 변환하여 사용해야 합니다. 극좌표에서 데카르트좌표로 변환하는 방법은 아래 그림과 같습니다.

 

[극좌표에서 데카르트 좌표로의 변환]
 
=====================================================
|   극좌표에서 데카르트 좌표로의 변환
|   벡터 A 가 ||A|| @ θ  일때
|   A = b1 i^  +  b2 j^
|   단, b1 = ||A|| cos θ , b2 = ||A|| sin θ
=====================================================
 
 그럼 예문을 하나 보면서 확실히 기억해볼까요?
벡터 A가 변위 벡터이고,  A = 20m @ 30˚ 일때  이 벡터를 데카르트 좌표 형식으로 변환해 보세요.
 
위의 공식을 잘 보세용 먼저 A를 두 요소로 분해하기 위해 사인과 코사인을 사용합니다.
 
b1 =   || A || cos θ    =     20 cos 30˚   =  20(0.8660)  =  17.32
b2 =   || A || sin θ    =    20 sin 30˚   =   20(0.5)   =   10
 
그래서 A = 17.32 i^  +  10 j^ 입니다.
 
 
 반대로 데카르트 좌표를 극좌표로 변환도 가능합니다.
========================================================
|   벡터 B 가   b1 i^ + b2 j^ 일 때,
 ||B|| = √(b1)² + (b2)² ,  θ = tan-1 (b2 / b1)     <--- tan-1 이 아크탄젠트라는거 알고 계시죠? ^^;;
========================================================
 
예제를 하나 살펴볼께요.
벡터 3i^ + 4j^ 를 극좌표로 변화하여 보세요.
 
먼저 B 의 크기를 계산합니다
||B|| = √(b1)² + (b2)²  =  √ 3² + 4²  =  √ 9 +16 = √25 = 5
 
B의 크기를 구한 다음에는 방향을 계산합니다.
θ = tan-1 ( b2 / b1 ) = tan-1 (4/3) = 53.1˚ (근사값입니다.)
 
따라서, 극좌표로는 B = 5 @ 53.1˚ 입니다.
 
 데카르트 좌표를 2차원에서 3차원으로 확장 할 수도 있습니다. 2차원에는 없는 z 축을 추가해주기만 하면 됩니다. 간단하죠? 공식은 다음과 같습니다.
 
====================================================================================
|   3차원 데카르트 좌표 ( 벡터 성분 )
|   벡터의 x 축 방향의 단위벡터를 i^  , y축 방향의 단위벡터를 j^  ,  z 축 방향의 단위벡터를  k^ 라고 하면,
|   벡터 B = b1 i^ + b2 j^ + b3 k^
=====================================================================================
 
 데카르트 좌표 형식의 벡터는 i, j 형식 대신 종종 행렬의 한 열이나 한 행으로 표기 되기도 합니다.
                                                                                    5
예를 들어보면 2차원 벡터 A = 5 i^  +  6 j^  는  [5,6]  또는  [  6  ] 으로 표기할 수 있습니다. 
                                                                                            7
마찬가지로 3차원 벡터 B = 7 i^  +  8 j^  +  9 k^ 는 [ 7, 8 ,9 ] 또는 [ 8 ] 라고 표기할 수 있습니다.
                                                                                            9
 
 
 
 
3 벡터의 합과 차
 
   벡터량을 시각적으로 구성할 때는, 각 벡터에 화살표를 사용하는 것이 일반적입니다. 화살의 길이는 벡터의 크기를 나타내고,  화살표의 끝이 가리키는 방향이 벡터의 방향을 나타냅니다. 화살은 비례에 맞도록 그려야 하기 때문에 모든 벡터가 같은 비례를 가지도록 그리는 것이 중요합니다.  아래의 그림처럼 크기가 5m 인 벡터는 크기가 10m 인  벡터의 절반에 해당하는 길이를 가지게 됩니다. 

[화살 표로 표현한 두 벡터]
 
 
벡터를 화살표로 표시할 때,  화살표의 촉 부분을 끝점이라고 하고 그 반대쪽을 시작점이라고 부릅니다.
벡터는 어느 한점에 고정된것이 아니기 때문에 화살표의 길이와 방향만 같다면 어디든지 이동이 가능합니다. 아래의 그림처럼 끝점이 같은 두 벡터 A 와 B에서 벡터 B의 끝점을 벡터 A의 시작점으로 옮기는 것도 가능합니다.

[벡터 A의 끝점과 벡터 B의 시작점을 일치하록 옮긴 벡터의 그림]

 

 

  두 벡터의 시작점과 끝점을 일치하도록 만들었다면, 움직이지 않은 벡터의 시작점과 움직인 벡터의 끝점을 연결하여 새로운 벡터를 그리면 이 새 벡터가 바로 두 벡터의 합입니다.


[ 벡터 A 와  벡터 B 의 합 ]
 
이렇게 벡터의 합을 구할때  둘중 어떤 벡터를 옮기더라도 새롭게 만들어지는 합의 벡터는 언제나 같습니다.
즉 벡터의 합에는 교환법칙이 성립한다는 이야기가 되는 것이죠.

[ A + B  =  B + A ]
 
 
=============================================
|   벡터 합의 교환법칙
|   임의의 벡터 A, B 에 대하여,
|   A + B  =  B + A
=============================================
 
 위의 그림에서 각각의 벡터 A 가 5m 라고 가정하고  벡터 B가 6m 라고 가정할때 A+B 의 벡터의 길이는 어떤가요? A=5 이고 B=6 이니 A+B =11 이라고 생각하기 쉽지만 그림을 잘 보시면 새롭게 만들어진 붉은 벡터가 기존의 두개의 검은 벡터를 더한것보다 짧게 보이죠? 이 것은 벡터의 방향이 고려되었기 때운입니다.
 
결국 방향이 같지 않다면  || A+B || ≠ ||A|| + ||B||   라는 말이 됩니다.
 
그래서 극 좌표로 표현된 벡터는 같은 방향이 아닌이상 항상 데카르트 좌표로 변환하여 더해야 합니다.
두 벡터가 데카르트 좌표로 표현되었다면, 같은 성분의 벡터끼리 더하기만 하면 됩니다.
 다시말해서 두개의 i^ 와 두개의 j^를 더하는 것이죠.
 
=================================================================
|  두 벡터의 합 계산
|  두 벡터 A = a1 i^ + a2 j ^  ,    B = b1 i^ + b2 j^ 에 대하여,
 A + B = ( a1 + b1 ) i^  +  (a2 + b2) j^
================================================================= 
 역시 예제를 살펴보면서 확실히 이해해 봅시다.
두 벡터 C = 8i^ + 3j^ ,   D = 5i^ + 12j^ 에 대하여, C+D를 구하세요.
 
 먼저 이 두 벡터를 그린후에 D의 시작점을 C의 끝점으로 옮깁니다. 거듭 강조하지만 벡터를 옮기는 과정에서 길이와 방향이 변하지 않도록 주의해 주세요.
 C 의 시작점과 D의 끝점을 이어서 새로운 벡터 C + D 를 그려줍니다. 그림은 아래와 같습니다.

 [ 벡터 C + D ]
 
C + D 를 계산하는 방법은  x축 방향으로의 총 이동량과  y축 방향으로의 총 이동량을 계산하는 것입니다.  즉 두개의 i^와 두개의 j^ 를 각각 성분별로 더하는 것이죠.  이 경우에는, 다음과 같이 계산할 수 있습니다.
 
C + D = (8+5) i^ + (3+12) j^  =  13 i^ + 15 j^
 
 벡터의 합을 구하는 이같은 방법은 3차원에서도 그대로 적용됩니다. 마찬가지로 3차원에서의 벡터의 합을 계산하려면 벡터는 반드시 데카르트 좌표 형식이어야 합니다.
 
=============================================================================
|  3차원 벡터의 합 계산
|  두 벡터 A = a1 i^ + a2 j^ + a3 k^,    B = b1 i^ + b2 j^ + b3 k^ 에 대하여,
|  A + B = (a1+b1) i^ + (a2+b2) j^ + (a3+b3) k^
=============================================================================
 
 지금까지 보아온 벡터의 합과 같은 방법으로 두 벡터의 차도 구할 수 있습니다. 각 성분의 합을 구한것처럼 각 성분의 차를 구하면 됩니다. 2차원과 3차원에서의 벡터의 차 공식 나갑니다!!!!!!!!]
 
=================================================================
|  두 벡터 사이의 차 계산
|  두 벡터 A = a1 i^ + a2 j ^  ,    B = b1 i^ + b2 j^ 에 대하여,
 A - B = ( a1 - b1 ) i^  +  (a2 - b2) j^
================================================================= 
=============================================================================
|  3차원 벡터의 차 계산
|  두 벡터 A = a1 i^ + a2 j^ + a3 k^,    B = b1 i^ + b2 j^ + b3 k^ 에 대하여,
|  A - B = (a1-b1) i^ + (a2-b2) j^ + (a3-b3) k^
=============================================================================
 
 
 
4. 벡터의 스칼라 곱
 
 우리들은 지금까지 공부하면서 수많은 종류의 곱셈을 해왔습니다. 양수끼리의 곱셈, 음수와 양수의 곱셈, 정수와 소수와 곱셈 등등등.......  이들 곱셈은 모두 스칼라값과 스칼라값 사이의 곱셈이었습니다.  이제 위에서 배운 백터를 써먹을 때가 됬네요.  바로 벡터와 스칼라의 곱셈을 할 차례입니다!!!!
 벡터에 스칼라를 곱한다는 것은 벡터의 크기를 늘리거나 줄이는 결과를 가져옵니다. 곱하는 스칼라가 1보다 크다면 벡터는 커지고, 스칼라가 1보다 작다면 벡터의 크기는 작아집니다.
 
============================================================================
|   극좌표 형식 벡터의 스칼라 곱
|   임의의 벡터 A = c ||A|| @ θ 에 대하여,
|   cA = c ||A|| @ θ
============================================================================
 
 예제를 하나 살펴볼까요?
 벡터 A = 3ft @ 22˚ 일때 5A 를 구하세요.
 
위의 공식에 바로 대입해 볼께요.
5A = 5(3ft) @ 22˚ = 15ft @ 22˚
 
벡터가 극좌표의 형식이 아닌 데카르트좌표 형식으로 되어 있어도 극좌표로 변환하지 않고 스칼라 곱을 구할 수 있는 방법이 있습니다.  각 성분별로 스칼라를 곱하기만 하면 됩니다. 공식은 아래와 같습니다.
 
=============================================================================
|  데카르트 형식 벡터의 스칼라 곱
|  임의의 스칼라 c와 임의의 벡터 A = a1 i^ + a2 j^ 에 대하여,
|  cA = ca1 i^ + ca2 j^
=============================================================================
 
예제 고고싱~~~!!!
벡터 A = 12 i^ + 4 j^ 일때, 1/2A 를 구하세요.
 
바로 공식에 대입합니다.
1/2A = 1/2(12 i^) + 1/2(4 j^) = 6 i^ + 2 j^
 
 
 프로그래밍에서는 정규화(normalization)라는 용어를 종종 쓰이곤 합니다. 이 용어는 벡터의 크기를 1로 맞추는 것을 뜻합니다. 벡터의 정규화는 벡터의 크기보다는 벡터의 방향만을 필요로 할 때 이용됩니다. B = i^ + j^ 에서 i^는 x 축의 양의 방향을,  j^ 는 y축의 양의 방향을 가리키는 크기가 1인 벡터입니다.
 
 벡터의 정규화는 벡터가 극좌표 형식일때는 매우 간단합니다. 단지 벡터의 크기를 1로 바꾸고 방향을 변경하지 않으면 됩니다. 예를 들어 3m @ 15˚ 의 벡터가 있다면 1m @ 15˚ 가 되는 것이죠. 너무 간단하죠? ^^;
 
 그렇지만 프로그래밍에서는 데카르트좌표로 표현되는 경우가 많습니다. 이런 경우에는 벡터의 크기를 계산하여 각 성분을 크기로 나누면 됩니다.
 
==============================================================================
|  2차원 벡터의 정규화
|  임의의 벡터 A = [a1 a2] 에 대하여
|  A^  = ( 1 / ||A|| ) A = [ a1 / ||A||    a2 / ||A|| ]
==============================================================================
 
==============================================================================
|  3차원 벡터의 정규화
|  임의의 벡터 A = [a1  a2  a3 ] 에 대하여,
|  A^ = ( 1 / ||A|| ) A = [ a1 / ||A||    a2 / ||A||    a3 / ||A|| ]
============================================================================== 
** 정규화된 벡터 표시인 A^ 역시 위의 i^, j^  와 마찬가지고 기호가 문자 위에 모자처럼 씌어야하는 것임을 알려드립니다.
 
 예제를 하나 볼까용?
 벡터 A = [5  0  -12] 를 정규하 하세요.
 
 가장먼저 해야 할 일은 A의 크기를 구하는 것입니다.
 ||A|| = √ 5² + 0² + (-12)²  =  √ 25 + 0 + 144 = √ 169 = 13
 
이제 크기를 알았으니 벡터의 각 성분을 크기로 나눕니다.
A^ = [ a1 / ||A||    a2 / ||A||   a3 / ||A|| ]  =  [ 5/13  0/13  -12/13 ]
 
벡터를 정규화 한 후에 답이 맞는지 검산하려면 ||A^|| 를 구해보면 됩니다. 이 결과는 항상 1이 나와야 합니다.
 
 
 
5. 벡터의 내적
 
 두 벡터의 내적은 그 결과가 항상 스칼라 값이 나옵니다. 그래서 다른 이름으로 스칼라적 이라고 부르기도 합니다. 
 
======================================================================
|  2차원 벡터의 내적
|  임의의 2차원 벡터 A = [ a1  a2 ] ,  B = [ b1  b2 ] 에 대하여,
|  A●B = a1 b1 + a2 b2
======================================================================
 
========================================================================
|  3차원 벡터의 내적
|  임의의 3차원 벡터 A = [ a1  a2  a3 ],  B = [ b1  b2  b3 ] 에 대하여,
|  A●B = a1 b1 + a2 b2 + a3 b3
========================================================================
 
벡터의 내적은 수 많은 정보를 제공합니다. 그 중 가장 먼저 살펴볼 부분이 바로 벡터의 내적을 통해 두 벡터 사이의 각을 구할 수 있다는 것입니다. 만일 두 벡터의 내적의 값이 0이다면 두 벡터는 서로 직교한다는 것을 알 수 있습니다. 그리고 두 벡터의 내적의 값이 0 보다 작다면 두 벡터 사이의 각은 90도 보다 큰것이고 0보다 크다면 두 벡터 사이의 각은 90도 보다 작게 되는 것입니다.
 
=========================================================================
내적의 부호
두 벡터 A 와  B 사이의 각을 θ 라고 할 때,
A●B < 0 이면 , θ > 90˚
A●B = 0 이면 , 서로 직교
A●B > 0 이면 , θ < 90˚
=========================================================================
 
 게임상에서 카메라의 위치가 (1,4)이고, 벡터 C = [ 5  3 ] 가 이 카메라의 방향을 나타내고 있습니다. 이 때 어떤 물체의 위치가 (7,2)이고 이 카메라의 시야가 90˚라면 이 물체는 카메라의 시야에 들어올까요?
 

[ 카메라의 시야 ]
 
먼저 위의 그림과 같이 카메라의 위치에서 물체의 위치를 가리키는 벡터를 구해야 합니다. 이 벡터를 D = [(7-1)  (2-4)] = [ 6  -2 ] 라고 합니다. 이제 벡터 C 와 벡터 D를 내적합니다.
 
 C●D = 5(6) + 3(-2) = 30 - 6 = 24
 
두 내적의 결과가 양수이므로 두 벡터 사이의 각 θ 는 90˚ 보다 작음을 알 수 있습니다. 카메라의 시야가 90˚이므로 이 물체는 카메라의 시야에 들어옴을 알 수 있습니다.
 
 간단한 계산만으로 두 벡터 사이의 각이 90˚ 에 대하여 큰지 작은지, 혹은 직교하는 지를 알 수 있었습니다. 만일 두 벡터 사이의 정확한 각을 구할 때에도 내적을 통해 알 수 있습니다.
 
===============================================================
|   두 벡터 사이의 각
|   임의의 두 벡터 A, B 사이의 각을 θ 라 하면,
|   A●B = ||A|| ||B|| cos θ
===============================================================
 
 예제를 볼께요.
 캐릭터가 벡터 C = [5  2  -3 ]가 가리키는 방향으로 이동중이었습니다. 그런데 플레이어가 새로운 목적지를 설정해 주어서 벡터 D = [8  1  -4 ]가 가리키는 방향으로 이동하게 되었습니다.  이 캐릭터는 얼마의 각을 꺽어야 새로운 경로로 이동 할 수 있을까요?
 
 위의 공식에 대입하려면 먼저 두 벡터의 내적을 구해야 합니다.
 C●D = 5(8) + 2(1) -3(-4) = 40 + 2 + 12 = 54
 
 이제 두 벡터의 크기를 각각 구해줍니다.
 ||C|| = √5² + 2² + (-3)² = √25 + 4 + 9 = √38
 ||D|| = √8² +1² + (-4)² = √64 + 1 + 16 = √81
 
이제 각각의 결과를 공식에 대입합니다.
C●D = ||C|| ||D|| cos θ
54 = (√38)(√81) cos θ
54 / (√38)(√81) = cos θ
54 / ( 6.16 * 9 ) = 54 / 55.44 = cos θ
θ = 13.08˚  <---근사값입니다.
 
따라서 이 캐릭터는 13.08˚ 꺽어서 움직이면 됩니다.
 
 
 
6. 벡터의 외적
 
 내적은 벡터를 곱하는 방법에 해당합니다. 이번에는 외적을 다뤄볼 차례 입니다.  내적과 외적의 가장 큰 차이점이라면 결과값의 형태이겠네요.  내적은 결과값이 스칼라이고 외적의 결과는 벡터입니다.
 
============================================================
|  외 적
|  임의의 두 벡터 A = [a1 a2 a3],  B = [b1 b2 b3]에 대하여,
|  A x B = [(a2b3 - a3b2) (a3b1 - a1b3) (a1b2 - a2b1)]
============================================================
 
예제를 하나 볼께요
 두 벡터 A = [ 5  -6  0 ],  B = [1  2  3 ]의 외적을 구하세요.
 
외적의 결과는 또 다른 벡터가 나오므로, 위의 공식을 사용하여 각 성분을 계산하면 됩니다.
A x B = [(a2b3 - a3b2)  (a3b1 - a1b3)  (a1b2 - a2b1)]
         = [(-6(3) - 0(2))   (0(1) - 5(3))   (5(2) - -6(1)) ]
         = [ (-18-0)  (0-15)  (10+6) ]
         = [-18  -15  16 ]
 
 
외적의 가장 중요한 성질은 바로 외적의 결과로 만들어진 벡터가 원래 두 벡터와 모두 직교하는 벡터라는 것입니다. 이러한 이유 때문에 외적은 3차원 벡터에 대해서만 정의 됩니다.
 
================================================================
|  직교 벡터
|  A x B 는 두 벡터  A, B 에 모두 수직
================================================================

[ 직교 벡터 ]
 
벡터 A 와 B 를 포함하는 평면에는 수직인 두개의 방향이 있습니다. 바로 위쪽과 아래쪽이죠. 외적의 결과 주어지는 벡터를 결정하는데 오른손 법칙을 쓸 수 있습니다. 손가락을 벡터 A와 B가 교차하는 부분에 두고 손가락을 벡터 A와 같은 방향으로 둡니다.  그런 후에 손가락이 벡터 B를 향하도록 구부리면,  이때 엄지가 향하는 방향이 AxB 의 방향입니다. 반대로 A와 B의 순서를 바꾸어 시도해보면 BxA는 방향이 정반대라는 것을 알 수 있습니다. 그래서 외적은 교환법칙이 성립하지 않습니다.
 
=====================================================================
 외적은 교환법칙이 성립하지 않음
|  A x B ≠ B x A
|  단, 임의의 3차원 벡터 A , B에 대해서 A x B = -(B x A)
=====================================================================
 
 외적은 결과가 원래의 두 벡터와 수직인 세번째 벡터를 만든다는 독특한 성질 덕뿐에 표면 법선(surface normal)을 계산하는데 사용할 수 있습니다. 임의의 두 3차원 벡터는 하나의 면을 정의합니다. 표면 법선은 주어진 면에 수직인 길이가 1인 벡터를 말합니다.
 
============================================================
|  표면 법선
|  임의의 두 3차원 벡터 A, B에 대하여,
|  표면 법선 = (A x^ B) = AxB / ||AxB||
=============================================================
**위의 공식에서 x^ 또한 위의 벡터의 부호처럼 ^ 이 x 위에 모자처럼 씌어져야 합니다
 
역시 예제를 살펴보면 실력을 다져 볼까요?
두 벡터 A = [5  -2  0 ], B = [1  2  3 ]으로 정의되는 면이 있을때, 이 면과 충돌한 물체의 운동을 결정하는데 사용할 수 있도록 표면 법선을 구하십시오.
 
외적의 결과는 주어진 두 벡터와 직교하는 새로운 벡터이죠? 먼저 외적을 합니다.
A x B = [ (a2b3 - a3b2)  (a3b1 - a1b3)  (a1b2 - a2b1) ]
          = [ (-2(3) - 0(2))   (0(1) - 5(3))   (5(2) - -2(1)) ]
          = [ (-6 - 0)   (0 - 15)   (10 + 2) ]
          = [ -6  -15  12 ]
 
외적을 구했으니 이제 외적의 크기를 구합니다.
||A x B|| = √(-6)² + (-15)² + (12)²
             = √36 + 225 + 144
             = √405
             = 20.125 
 
이제 공식에 대입하면 됩니다.
[ -6/20.125   -15/20.125   12/20.125 ] = [0.298   0.745   0.596 ]
 
 
이미 위에서 두 벡터 사이의 각을 내적을 사용하여 구하는 법을 배웠습니다. 각을 구하는 데에는 내적이 더 빠릅니다만 이미 외적의 값을 알고 있다면 외적을 사용하여 각을 구하는 법을 사용하는것이 좋습니다.
 
=========================================================================
|   두 벡터 사이의 각(외적)
|   임의의 두 3차원 벡터 A, B에 대하여
|   ||AxB|| = ||A|| ||B|| sin θ
=========================================================================
 
자 예제 나갑니다. 단 이번에는 풀이가 없고 답만 알려드릴께요. 한번 직접 풀어보시고 답이랑 맞는지 맞춰보세요 ㅋㅋㅋ
 
<문제> 두 벡터 A = [5  -2  0],  B = [1  2  3] 사이의 각을 구하세요.
<답> 약 87.2˚ 
 
////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//////
 
에고 에고 평상시보다 양이 배는 많은것 같네 ㅠㅠ 갈수록 올리는 속도가 느려지네 -.-a
 



 









아래 설명의 출처 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) 이다

벡터의 곱이 스칼라 이면 내적이고 벡터의 곱이 벡터이면 외적이다


반응형

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

회전 변환  (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 engines

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




실제로 Direct 와 Opengl(수학좌표계) 에서의 외적 연산은 동일하다

 

연산은 동일한데 포인트는, 기본 좌표계에따라 외적의 방향이 달라진다는 것

 

 

 

 


이하 첨부내용

 

http://blog.naver.com/9992028/120020809381

 

왼손 좌표계 vs 오른손 좌표계
OpenGL은 오른손 좌표계를 쓰고,
DirectX는 왼손 좌표계를 쓴다.

사실
DirectX를 쓰면서도 오른손 좌표계를 쓰거나,
OpenGL을 쓰면서 왼손 좌표계를 사용하는 것도 가능하다.

다만 기본값이 그렇게 되어 있다는 것이고,
따라서 각각의 좌표계에 좀 더 편리하게 API가 제공되는 것일 뿐이지만 말이다.

간단하게 다음과 같이 이해하자.

일단 수학적으로는 다음과 같이 정의 된다.

기저 vector, V1, V2, V3가 있을 때.. (간단하게 x, y, z 방향이라 생각해도 된다.)
(V1 X V2) · V3 > 0 이면 오른손 좌표계
(V1 X V2) · V3 < 0 이면 왼손 좌표계이다.

정규 직교 좌표계로 한정 (세 기저 vector가 서로 직각인 경우) 지어서
기저 vector, Vx, Vy, Vz가 있을 때..
(Vx X Vy) = Vz 이면 오른손 좌표계
(Vx X Vy) = -Vz 이면 왼손 좌표계이다.

따라서 

OpenGL의 경우 (오른손 좌표계인 경우)
x, y 방향의 기저 vector을 아는 경우 z 방향의 기저 vector는 -(Vx X Vy)로 구하고,

DirectX의 경우 (왼손 좌표계인 경우)
x, y 방향의 기저 vector을 아는 경우 z 방향의 기저 vector는 (Vx X Vy)로 구하면 되겠다.
 
 
 
 
< 자료출처>  http://mobile.egloos.com/1848128

반응형

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

일차 독립, 일차 종속  (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
반응형

http://hsengine.co.kr/20149811131

 

 

 


 CEGUI 설치와 CEGUI VC++ 설정

 

cegui 설치방법과 cegui 설정하는 방법에 대해 설명하겠다.

CEGUI를 다운받기 위해 CEGUI 사이트에 접속한다.

 

http://www.cegui.org.uk/wiki/index.php/Downloads 

 

 

 

 

Crazy Eddie's GUI System - Downloads 에서 최신버전을 클릭한다.

 

Library Source Downloads에서 운영체제에 맞는 소스코드를 다운받는다.

 

그리고 Binary Dependency Downloads for MSVC++에서 VC++버전에 맞는것을 찾아 다운받는다.

 

 

 

Library Source Downloads에서 다운받는 압축파일을 풀고. (예: C:\CEGUI)

 

Binary Dependency Downloads for MSVC++에서 받은 압축파일을 열어 dependencies 폴더안에 있는 파일들을 아까 압축푼곳에 압축을 풀어 덮어쓴다. 

 

이제 C:\CEGUI\projects\premake 로 이동한다.

 

여기서 오우거엔진이나 일리히트엔진에 이용할꺼면 config.lua 파일을 수정해야된다.

 

config.lua파일에 찾아보면 아래와 같은 내용이 있다.

 

자신에 맞게 직접 수정해주면 된다. (Direct3D9을 이용할꺼면 따로 수정하지 않아도 된다.)

 

그리고 오우거나 일리히트엔진은 따로 경로 설정을 해야된다.

 

 

 

이제  build_vs2008.bat 파일와 build_samples_vs2008.bat을 실행한다. (버전에 맞는걸로 실행)

 

 

 

 

 

그러면 CEGUI.sln, CEGUISamples.sln 파일이 생성된다.

 

열어서 빌드만 해주면 된다.

 

C:\CEGUI\bin 폴더에서 Samples 들을 실행해볼 수 있다.

 

 

VC++ 설정

 

 

 

lib

C:\CEGUI\lib

C:\CEGUI\lib\dynamic

C:\CEGUI\lib\ReleaseWithSymbols

C:\CEGUI\lib\static

 

include

C:\CEGUI\include

C:\CEGUI\cegui\include

C:\CEGUI\cegui\include\elements

C:\CEGUI\cegui\include\falagard

C:\CEGUI\cegui\include\RendererModules\Direct3D9 (여기서 Direct3D9 대신 Irrlicht, Ogre, OpenGL, Direct3D10 등.. 추가할 수 있다. 자신이 사용할것에 따라 설정한다.)

 


directx 는 render 프로젝트에 포함시켜주면된다 CEGUIBase 에도 해놨는데 없어도 되는지는 확인해봐야암

반응형

'그래픽스(Graphics) > GUI Library' 카테고리의 다른 글

cegui 튜토리얼 번역  (0) 2013.02.04
GUI 제공하는 사이트들  (0) 2012.11.02
반응형

출처 : hppt://cafe.naver.com/cafec/248627



직접 구한할려면 보통 일이 아닙니다. 그래서 이미 만들어진 라이브러리를 이용하곤 하죠.
대표적으로 
CEGUI(http://www.cegui.org.uk/wiki/index.php/Main_Page 너무 기능이 많아서 무겁습니다)
librocket(http://librocket.com/ )
guichan(http://guichan.sourceforge.net/ 중국사람이 만든건데 요즘 업데이트 안하는듯)
Scaleform(http://gameware.autodesk.com/scaleform/ flash 이용, 가격이 천만원 초반대ㄷㄷ, 요즘 대부분 온라인게임에 탑제되어서 거의 트렌드입니다.) 
등등,,, 
html, javascript를 이용하는것도 있고, 각양각색입니다. 구글신 검색해보면 많이 나옵니다.

반응형

'그래픽스(Graphics) > GUI Library' 카테고리의 다른 글

cegui 튜토리얼 번역  (0) 2013.02.04
CEGUI 설치  (0) 2012.11.02
반응형

//Slices는 나누어질 삼각형의 갯수 이며 Stack은 사진에서 Y축으로 나누어질 갯수
 if( FAILED(D3DXCreateSphere(_pD3dDevice,5,8,4,&_sphere, NULL)) ){
   AfxMessageBox(L"스피어 생성 실패");
}

반응형

'그래픽스(Graphics) > DirectX9~12' 카테고리의 다른 글

Direct3D9에서 큐브 환경맵 만들어 보기  (0) 2012.11.08
환경 맵(큐브 맵, Cube Map)  (0) 2012.11.08
투명처리와 알파 합성  (0) 2012.11.02
점 그리기 -크기 변환  (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 );

 


반응형
반응형

칼라키로 뺀다
  * 텍스쳐 생성시 칼러키를 지정하여 빼기

D3DCOLOR ColorKey = D3DCOLOR_XRGB(255,0,255);
D3DXCreateTextureFromFileEx( DXD9, 이름,  크기, 크기, 1, NULL,텍스쳐포맷, 메모리풀, 필터, 필터, ColorKey, NULL, NULL, 텍스쳐포인터);


픽셀에 투명값이 있다
  * 포토샵에서 저장시 알파채널을 포함해서 저장
  * TGA, PNG, DDS등의 포맷을 사용
  * SetRenderState에 알파를 사용한다는 옵션을 선택

m_pD3dD9->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE);
m_pD3dD9->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
m_pD3dD9->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);



SetTextureStage에서 FACTOR로 투명도를 조절한다
  * 이미지가 가지고 있는 알파값에 다시 알파값을 조정할수 있다.
  * 픽셀칼러(RGB) * 픽셀알파값(100%로계산) * 원하는 알파값(100%로계산)

m_pD3dD9->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
m_pD3dD9->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
m_pD3dD9->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
m_pD3dD9->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_ARGB( 원하는 알파값, 255, 255, 255 ) );



정점의 알파값을 사용한다
  * 정점의 칼라가 ARGB를 사용할경우 0xffffffff인데
  * 맨앞의 ff값을 수정해주면, 채워지는 face의알파도 영향을 받는다.
  * 픽셀칼러(RGB) * 픽셀알파값(100%로계산) * 정점알파값(100%로계산)

m_pD3dD9->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
m_pD3dD9->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

m_pD3dD9->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

 

 

 

 

 

 


http://www.silverwolf.co.kr/4886

 

 

[알파합성]

 

2번 렌더링을 실시하는 , 이른바 투패스 라고 부르는 방법으로 알파 합성(알파 블렌딩) 을 의미 알파 합성이란 폴리곤을 렌더링할때, 폴리곤 색을 그냥 출력하는 것이 아니라 이미 렌더링 된 화면에 여러가지 합성을 해서 출력하는 방법이다. 

알파합성은 현재 그려져 있는 색과 렌더링하는 폴리곤의 색을 합성한다. 따라서 , 반투명 폴리곤을 렌더링하기 전에 불투명 오브젝트를 렌더링 하여야 한다.   

깊이 테스트를 하면서 렌더링 할 경우 , 렌더링 순서를 잘못하면 반 투명 폴리곤의 한쪽 부분은 오브젝트가 렌더링 되지 않고, 마술처럼 오브젝트의 일부분이 사라진 화면이 출력되게 됨. 

------------------------------------------------------------------------------------- 

반투명 on : 디바이스->SetRenderState(D3DRS_ALPHABLENDABLE , TRUE); 
반투명 OFF : 디바이스->SetRenderState(D3DRS_ALPHABLENDABLE , FLASE); 

------------------------------------------------------------------------------------- 
합성 설정 방법 : 디바이스->SetRenderState(D3DRS_BLENDOP , 값) 

D3DBLENDOP_ADD 
                          최종색 = A * 렌더링할 폴리곤 색 + B * 이미 렌더링 된 색 

D3DBLENDOP_SUBTRACT 
                          최종색 = A * 렌더링할 폴리곤 색 - B * 이미 렌더링 된 색 

D3DBLENDOP_REVSUBTRACT 
                          최종색 = -A * 렌더링할 폴리곤 색 + B * 이미 렌더링 된 색 

D3DBELNDOP_MIN 
                         최종색 = 작은값(A * 렌더링할 폴리곤 색 , B * 이미 렌더링 된 색) 

D3DBELNDOP_MAX 
                         최종색 = 큰값(A * 렌더링할 폴리곤 색 , B * 이미 렌더링 된 색) 

-------------------------------------------------------------------------------------- 
아무 것도 설정되지 않았을때는 D3DBLENDOP_ADD 가 사용되며 , D3DBLENDOP_ADD 는 색을 서로 더한다. 

합성 강도 또한 SetRenderState 로 설정한다. 
이제부터 렌더링 하려는 폴리곤의 합성 강도 
디바이스->SetRenderState(D3DRS_SRCDBLEND , 값A) 

이미 렌더링 된 렌더일 타겟 색의 합성 강도 
디바이스->SetRenderState(D3DRS_DESTBLEND , 값B) 

--------------------------------------------------------------------------------------- 
<다양한 합성 방법> 

1. 선형 합성 

이른바 , 알파 합성이라 부르는 합성방법입니다. 이미 그려진 색과 지금부터 렌더링 하려고 하는 폴리곤의 색을 다음과 같은 비율로 합성합니다. 

최종색 = (1 - a) * 바닥색 * a * 덮일색. 

// 선형합성 C = Cd(1-As)+CsAs 
RS(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 
RS(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 

--------------------------------------------------------------------------------------- 
덧셈 합성 

이합성은 바닥색의 변함없이 폴리곤의 색을 위에 겹칩니다. 

최종색 = 바닥색 + a * 덮일색 

a의 값이 커지는 것 만큼 , 위에 그려지는 폴리곤을 강하게 합성한다. 

RS(D3DRS_DESTBLEND, D3DBLEND_ONE); 
RS(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 

이상의 합성은 빛의 표현에 많이 사용, GLARE 효과. 

--------------------------------------------------------------------------------------- 

뺄셈 합성 

최종색 = 바닥색 - a* 덮일색 

// 뺄셈합성 C = Cd-CsAs 
RS(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT); 
RS(D3DRS_DESTBLEND, D3DBLEND_ONE); 
RS(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 

이 합성 방법은 그림자 표현에 사용됨. 

뺄셈 합성을 하는 폴리곤을 여러번 거듭해나가면, 마지막에는 까맣게 된다. 
---------------------------------------------------------------------------------------- 
곱셈 합성 

최종색 = 바닥색 * 덮일색 

// 곱셈합성 C = Cd*Cs 
RS(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR); 
RS(D3DRS_SRCBLEND, D3DBLEND_ZERO); 
---------------------------------------------------------------------------------------- 

제곱 합성 

어두운곳은 더 어둡게 하고, 밝은 곳은 그대로 표시함. 이를 식으로 표현하면 다음과 같음. 

최종색 = 바닥색 * 바닥색 

바닥색을 제곱해서 색의 채도를 강조함. 제곱 합성은 렌더링 하는 폴리곤의 색이나 알파 값에 영향을 받지 않기 때문에, 폴리곤을 입혀 렌더링해도 텍스쳐는 효과를 미치지 않음. 

RS(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR); 
RS(D3DRS_SRCBLEND, D3DBLEND_ZERO); 

---------------------------------------------------------------------------------------- 
네거티브 포지티브 반전 

흰 폴리곤을 렌더링하면, 검은 부분은 희게, 흰 부분은 검게 출려되도록 색을 역전하는 변환을 합니다. 

최종색 = ( 1 - 바닥색) * 덮일색 

RS(D3DRS_DESTBLEND, D3DBLEND_ZERO); 
RS(D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR); 
---------------------------------------------------------------------------------------- 

불투명 

알파블렌딩을 설정한 채로 불투명한 것을 그리고 싶을때 

최종색 = 덮일색 

RS(D3DRS_DESTBLEND, D3DBLEND_ZERO); 
RS(D3DRS_SRCBLEND, D3DBLEND_ONE); 

 
 
 
 
 
D3DRS_ALPHATESTENABLE // 알파테스트 사용 유/무 디폴트: FALSE 
D3DRS_ALPHAREF // 시험 픽셀값 0x00 ~ 0xff 
D3DRS_ALPHAFUNC // D3DCMPFUNC에 대한 정의 디폴트: D3DCMP_ALWAYS 

D3DCMPFUNC 

D3DCMP_NEVER 항상시험에 실패한다. 
D3DCMP_LESS 시험 픽셀이 현재의 픽셀보다 작은 경우 통과한다. 
D3DCMP_EQUAL 시험 픽셀이 현재의 픽셀과 동일한 경우 통과한다. 
D3DCMP_LESSEQUAL 시험 픽셀이 현재의 픽셀보다 작거나 같은 경우 통과한다. 
D3DCMP_GREATER 시험 픽셀이 현재의 픽셀보다 큰 경우 통과한다. 
D3DCMP_NOTEQUAL 시험 픽셀이 현재와 같지 않은 경우 통과 한다. 
D3DCMP_GREATEREQUAL 시험 픽셀이 현재보다 크거나 같은 경우 통과한다. 
D3DCMP_ALWAYS 항상 시험에 통과한다. 


# 시험 픽셀은 D3DRS_ALPHAREF의 값이며 현재 픽셀은 출력될때 이미지의 알파픽셀의 값을 의미 합니다. 

# 시험에서 통과하지 않을경우는 그에 해당하는 현재픽셀은 무시하게 되어 출력하지 않습니다. 

통과 했을 경우는 현재픽셀의 알파값을 가지고 출력하게 됩니다. ( 말 그대로 알파 비교를 하게 되겠지여 ) 

알파 테스트는 픽셀에 들어있는 알파값을 비교해서 이 픽셀을 사용할것인지 안할것인지를 결정하게 되는 겁니다. 

이것을 잘만 사용하면 스프라이트 형식의 1555 포맷과 같은 이미지 형식이라면 알파소팅을 하지 않아도 됩니다. 

P.S 알파 테스트를 하는 목적중에 하나는 렌더링 속도를 올리기 위해서 입니다. 

이미지에서 알파를 사용하면 자신의 위치에서 뒤에 있는 이미지의 픽셀을 혼합해서 나오게 되는데 

알파테스트는 불필요한 혼합을 하지 않아도 되므로 속도 향상을 줄수가 있는 겁니다. 

설명이 제대로 됬는지 모르겠군여 틀린부분 있으면 질문&답변게시판에 지적해 주세여 
----------------------------------------------- 

벌날개의 메모... 
<알파채널이 있는 경우...(TGA)> 
텍스처 로드...(초기화에서) 
if( FAILED( D3DUtil_CreateTexture( g_pd3dDevice,"tree01s.tga", 
                                           &g_pTexture ) ) ) 
            return E_FAIL; 
(랜더링에서) 
g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE ); 
g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA ); 
g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); 

랜더링..... 

 
 
 
 
텍스쳐 알파 채널을 이용한 투명 표현
 

// 선형합성 C = Cd(1-As)+CsAs

theApp.m_pDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
theApp.m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); 
theApp.m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );


블렌딩이 안되는건 텍스쳐 파일에 알파채널 값이 없기 때문이였다. -_-!


그리고 그 전에 컬러끼리 섞었는데 그 결과는 좀 진한 투명 색이였다.

두 색이 합쳐진건지 원.. ㅋ

theApp.m_pDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
theApp.m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR ); 
theApp.m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVDESTCOLOR );





알파 블렌딩에 관하여


눈에는 보이지 않는...일종의 색상들을 섞을때 파라메터로 사용되는...그런 '값'이죠.

예를 들어 D3D의 알파블렌딩은 아래와 같이 이루어집니다.

WriteColor = SrcBlend * SrcColor + DstBlend * DstColor ;

WriteColor : 화면에 찍고자 하는 색상
SrcColor  : 텍스쳐 블렌딩(쉐이딩,멀티텍스쳐링 따위...)처리된 텍스쳐색상
DstColor  : 현재 화면에 찍혀있는 색상

SrcBlend와 DstBlend를 적당히 잘 만들어 주면 재미있는 효과를 얻을수 있습니다.

SrcBlend = 1  DstBlend = 0 
그냥 색상을 찍음.

SrcBlend = 0  DstBlend = 1 
아무색상도 찍지 않음.

SrcBlend = SrcAlpha  DstBlend = 1-SrcAlpha 
SrcColor에 있는 알파값으로 블렌딩.
( 만약 모든 SrcAlpha값이 0.5 라면  반반씩 섞이는 셈이니...정말로 반.투.명)

SrcBlend = 1  DstBlend = 1
SrcColor와 DstColor를 그냥 더해서 찍음.
텍스쳐가 찍히는 곳은 원래 찍혀있던 색상보다 밝아집니다. 눈부시게 빛나는(광원)효과.

SrcBlend = 0  DstBlend = SrcColor
SrcColor와 DstColor를 그냥 곱해서 찍음.
텍스쳐의 색상으로 원래 찍혀있는 색상을 쉐이딩한다고 생각하시면 쉽죠.
멀티텍스쳐가 지원되지 않을때 라이트매핑따위.

음.........
따지고 보니 알파블렌딩과 알파값은 사실 별 상관이 없어보이죠?
맞습니다.
그냥 텍셀마다 하나의 '확장 매개변수값'을 가지고 있다고 생각하세요.
이걸 가지고 무얼하는지는 순전히 프로그래머 맘이죠.


반응형

'그래픽스(Graphics) > DirectX9~12' 카테고리의 다른 글

환경 맵(큐브 맵, Cube Map)  (0) 2012.11.08
Direct 에서 스피어함수  (0) 2012.11.02
점 그리기 -크기 변환  (0) 2012.11.02
점그리기  (0) 2012.11.02
Directx D3DXCreateSphere 구현  (0) 2012.11.02
반응형

//점의 색상에 알파값을 적용하기 위한state
   _pD3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
   _pD3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
   _pD3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );

   
   _pD3dDevice->SetRenderState(D3DRS_POINTSPRITEENABLE,TRUE);//점이 스프라이트로 그려지기 때문에 텍스쳐 가능을 켜야 한다
   _pD3dDevice->SetRenderState(D3DRS_POINTSCALEENABLE,TRUE);//크기 크게 가능 

   //이 세 개의 상수는 거리에 따라 포인트 스프라이트의 크기가 변하는 방법을 제어한다. 야기에서 말하는 거리란 카메라와 포인트 스프라이트 간의 거리이다. 
   //이것을 쓰려면 A=x,B=y,C=z 세개다 몇시해 줘야 한다
   _pD3dDevice->SetRenderState(D3DRS_POINTSCALE_A,0); 
   _pD3dDevice->SetRenderState(D3DRS_POINTSCALE_B,0); 
   _pD3dDevice->SetRenderState(D3DRS_POINTSCALE_C,1);


   float PointSize = 10.0f;
   _pD3dDevice->SetRenderState( D3DRS_POINTSIZE , *((DWORD*)&PointSize));
   _pD3dDevice->SetRenderState(D3DRS_POINTSIZE_MAX,*((DWORD*)&PointSize));   //max 가 0 이 아니어야 한다
   
   PointSize = 0.0f;
   _pD3dDevice->SetRenderState(D3DRS_POINTSIZE_MIN, *((DWORD*)&PointSize));

   _pD3dDevice->SetStreamSource( 0, NULL, 0, sizeof(VERTEX_POSCOLORPOINT) );
   _pD3dDevice->SetFVF( D3DFVF_POS_DIFFUSE );
   _pD3dDevice->SetIndices( NULL );

   _pointNodes[0]._color=D3DCOLOR_ARGB(33,0xff,0xff,0xff);
   _pD3dDevice->DrawPrimitiveUP( D3DPT_POINTLIST,_pointNodes.size(),(void*)&_pointNodes[0], sizeof(VERTEX_POSCOLORPOINT) );

   PointSize = 1.0f;
   _pD3dDevice->SetRenderState( D3DRS_POINTSIZE , *((DWORD*)&PointSize));
   _pD3dDevice->SetRenderState(D3DRS_POINTSCALEENABLE,FALSE);
   _pD3dDevice->SetRenderState(D3DRS_POINTSPRITEENABLE,FALSE); 
   _pD3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
   _pD3dDevice->SetRenderState(D3DRS_POINTSCALE_C,0);

 

 

 

float 를 DWORD 로 변환할때 &,* 를 사용하는 이유

 

 

*(DWORD*)&PointSize 로 해야 제대로 소수부분또한 실수로 변환되어 DWORD 에서 실수에서의 값을 갖고 있게 된다

즉 *(DWORD*)&PointSize  에서 넘겨지는 DWORD 는 float 형을 DWORD의 비트열로 변환한 값을 필요로 한다는 것.

 

 

반응형

'그래픽스(Graphics) > DirectX9~12' 카테고리의 다른 글

Direct 에서 스피어함수  (0) 2012.11.02
투명처리와 알파 합성  (0) 2012.11.02
점그리기  (0) 2012.11.02
Directx D3DXCreateSphere 구현  (0) 2012.11.02
디바이스를 잃었을때의 복구 절차  (0) 2012.11.02
반응형

typedef struct _VERTEX_POSCOLOR
{

D3DXVECTOR3 pos;

DWORD color;
 static const DWORD FVF_POS_DIFFUSE;  //==D3DFVF_POS_DIFFUSE
public :
 //생성자로 인수받아 초기화 해준다
}VERTEX_POSCOLOR;


 

 

VERTEX_POSCOLOR g_Vertices[3];

 

g_Vertices[0]=VERTEX_POSCOLOR(-10.0f,-10.0f, 0.0f, 0xffffffff);
  g_Vertices[1]=VERTEX_POSCOLOR(10.0f,-10.0f, 0.0f, 0xffffffff);
  g_Vertices[2]=VERTEX_POSCOLOR( -5.0f, 10.0f, 0.0f, 0xffffffff);

 

 

 

 

 

_pD3dDevice->SetFVF( VERTEX_POSCOLOR::FVF_POS_DIFFUSE );
 _pD3dDevice->DrawPrimitiveUP( D3DPT_POINTLIST,3, (void *)g_Vertices, sizeof(VERTEX_POSCOLOR) );

반응형
반응형

http://ergate.tistory.com/117

 

 



Directx 에서 지원하는 Sphere함수를 구현해보았습니다.

Slices는 나누어질 삼각형의 갯수 이며 Stack은 사진에서 Y축으로 나누어질 갯수 입니다.

Radius는 반지름의 값이며 Slices Stack Radius 의 값을 조정하시면 그 값에 맞게 Sphere가 그려집니다.

그리는 방식은 Vertices로 각 위치의 Point를 찍어주며 이것을 index buffer르 사용하여 사과의 껍질을 벗겨내는 형식으로 

각포인트를 이어서 삼각형을 만들어 주시면 됩니다.




 

#include <Windows.h>
#include <mmsystem.h>
#include <d3dx9.h>

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D = NULL; 
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; 
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
LPD3DXMESH                  g_pMesh   = NULL; 
LPDIRECT3DINDEXBUFFER9      g_pIndexVB = NULL;

struct CUSTOMVERTEX
{
 FLOAT x, y, z;     
 DWORD color;       
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)


struct INDEX
{
 WORD _0, _1, _2;
};

int Slices = 6;
int Stack = 4;
int numvertex = (Slices+1)*2+((Stack-3)*Slices);
int numface = (Slices*2)+((Stack-2)*2)*Slices;

 


//-----------------------------------------------------------------------------
// Name: InitD3D()
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
 if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
  return E_FAIL;

 D3DPRESENT_PARAMETERS d3dpp;
 ZeroMemory( &d3dpp, sizeof( d3dpp ) );
 d3dpp.Windowed = TRUE;
 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
 d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

 if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
  &d3dpp, &g_pd3dDevice ) ) )
 {
  return E_FAIL;
 }

 g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

 g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME  );

 g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

 return S_OK;
}

 

//-----------------------------------------------------------------------------
// Name: InitGeometry()
//-----------------------------------------------------------------------------
HRESULT InitGeometry()
{

 D3DXCreateSphere( g_pd3dDevice, 2.0f, 5, 4, &g_pMesh, NULL );  

 float Radius = 2.0f;

 CUSTOMVERTEX *g_Vertices = new CUSTOMVERTEX[numvertex];


 g_Vertices[0].x = 0.0f;
 g_Vertices[0].y = Radius; 
 g_Vertices[0].z = 0.0f;
 g_Vertices[0].color = 0xffffffff;

 for( int i=0+1, k=1; i<Stack; ++i )
 {
  D3DXVECTOR3 tmp( 0.0f, Radius * cosf( ( D3DX_PI / Stack ) * i ), Radius * sinf( ( D3DX_PI / Stack ) * i ));
  for( int j=0; j<Slices; ++j, ++k )
  {
   float temp = Radius - ( Radius - tmp.z );
   g_Vertices[k].x = temp * cosf(D3DXToRadian( (360/Slices) * j ));
   g_Vertices[k].y = tmp.y;
   g_Vertices[k].z = temp * sinf(D3DXToRadian( (360/Slices) * j ));
   g_Vertices[k].color = 0xffffffff;
  }
 }


 g_Vertices[numvertex-1].x = 0.0f;
 g_Vertices[numvertex-1].y = -Radius;     
 g_Vertices[numvertex-1].z = 0.0;
 g_Vertices[numvertex-1].color = 0xffffffff;


 if( FAILED( g_pd3dDevice->CreateVertexBuffer( numvertex * sizeof( CUSTOMVERTEX ),
  0, D3DFVF_CUSTOMVERTEX,
  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
 {
  return E_FAIL;
 }

 VOID* pVertices;
 if( FAILED( g_pVB->Lock( 0, sizeof( g_Vertices ), ( void** )&pVertices, 0 ) ) )
  return E_FAIL;
 memcpy( pVertices, g_Vertices, sizeof( CUSTOMVERTEX ) * numvertex);
 g_pVB->Unlock();


 int j=0;
 int z=0;

 INDEX *index = new INDEX[numface];

 for(z=0; z<Slices; ++z)
 {   
  index[z]._0 = 0;
  index[z]._1 = z+1;
  index[z]._2 = z+2-j;
  if( z == Slices - 2 )
  {
   j = Slices;
  }
 }

 int t = z;
 int tmp = 0;
 int tmp2 = 0;
 int a = 0;


 for( int i=0; i<Stack - 2; ++i )
 {
  tmp = (Slices + 1) + ( i * Slices );
  tmp2 = 2 + ( i * Slices ); 
  for( int j=0; j<Slices; ++j )
  {
   if( j == (Slices - 1) )
   {
    a = Slices;
   }
   index[t]._0 = tmp2 + j - a;         
   index[t]._1 = tmp2 + j - 1;         
   index[t]._2 = tmp2 + j + Slices - a;       

   index[t+1]._0 = tmp + j + 1 - a;                
   index[t+1]._1 = tmp + j - ( Slices );   
   index[t+1]._2 = tmp + j;    
   t+=2;
  }
  a = 0;
 }

 int b;

 b = t;

 int q = 0;
 for( int i=0; i<Slices; ++i )
 {
  index[b]._0 = tmp + i;
  index[b]._1 = tmp + Slices;
  index[b]._2 = tmp + i + 1 - q;
  if( i  == Slices - 2 )
  {
   q = Slices - i;
  }
  b++;
 }

 

 if( FAILED( g_pd3dDevice->CreateIndexBuffer( numface * sizeof(INDEX), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIndexVB, NULL ) ) )
 {
  return E_FAIL;
 }

 VOID* pIndices;
 if( FAILED( g_pIndexVB->Lock( 0, sizeof(INDEX)*numface, (void**)&pIndices, 0 ) ) )
  return E_FAIL;
 memcpy( pIndices, index, sizeof(INDEX)*numface );
 g_pIndexVB->Unlock();


 return S_OK;
}


//-----------------------------------------------------------------------------
// Name: Cleanup()
//-----------------------------------------------------------------------------
VOID Cleanup()
{


 if( g_pIndexVB != NULL )
  g_pIndexVB->Release();

 if( g_pMesh != NULL )
  g_pMesh->Release();

 if( g_pVB != NULL )
  g_pVB->Release();

 if( g_pd3dDevice != NULL )
  g_pd3dDevice->Release();

 if( g_pD3D != NULL )
  g_pD3D->Release();
}

 

//-----------------------------------------------------------------------------
// Name: SetupMatrices()
//-----------------------------------------------------------------------------
VOID SetupMatrices()
{
 D3DXMATRIXA16 matWorld;


 UINT iTime = timeGetTime() % 9000;
 FLOAT fAngle = iTime * ( 2.0f * D3DX_PI ) / 9000.0f;
 D3DXMatrixRotationY( &matWorld, fAngle );
 g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );


 D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-10.0f );
 D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
 D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );

 D3DXMATRIXA16 matView;
 D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
 g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );


 D3DXMATRIXA16 matProj;
 D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f );
 g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

 

//-----------------------------------------------------------------------------
// Name: Render()
//-----------------------------------------------------------------------------
VOID Render()
{
 g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0f, 0 );

 if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
 {
  SetupMatrices();

 

  g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
  g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
  g_pd3dDevice->SetIndices( g_pIndexVB );

  //  g_pd3dDevice->DrawPrimitive( D3DPT_POINTLIST, 0, numvertex );

  g_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, numvertex, 0, numface );


  g_pd3dDevice->EndScene();
 }

 g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

 


//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
 switch( msg )
 {
 case WM_DESTROY:
  Cleanup();
  PostQuitMessage( 0 );
  return 0;
 }

 return DefWindowProc( hWnd, msg, wParam, lParam );
}

 


//-----------------------------------------------------------------------------
// Name: WinMain()
//-----------------------------------------------------------------------------
INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
{
 UNREFERENCED_PARAMETER( hInst );

 WNDCLASSEX wc =
 {
  sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc, 0L, 0L,
  GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
  L"D3D Tutorial", NULL
 };
 RegisterClassEx( &wc );

 HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 03: Matrices",
  WS_OVERLAPPEDWINDOW, 100, 100, 800, 600,
  NULL, NULL, wc.hInstance, NULL );

 if( SUCCEEDED( InitD3D( hWnd ) ) )
 {
  if( SUCCEEDED( InitGeometry() ) )
  {
   ShowWindow( hWnd, SW_SHOWDEFAULT );
   UpdateWindow( hWnd );

   MSG msg;
   ZeroMemory( &msg, sizeof( msg ) );
   while( msg.message != WM_QUIT )
   {
    if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
    {
     TranslateMessage( &msg );
     DispatchMessage( &msg );
    }
    else
     Render();
   }
  }
 }

 UnregisterClass( L"D3D Tutorial", wc.hInstance );
 return 0;
}

반응형

'그래픽스(Graphics) > DirectX9~12' 카테고리의 다른 글

점 그리기 -크기 변환  (0) 2012.11.02
점그리기  (0) 2012.11.02
디바이스를 잃었을때의 복구 절차  (0) 2012.11.02
그래픽카드 디바이스. D3D_Device  (0) 2012.11.02
Directx3D 에서 2D 점 그리기  (0) 2012.11.02
반응형

 
Lost Device
① 원리
-> Full Screen Mode에서 ALT+TAB을 눌러 Task를 전환하면,
 윈도우는 자동적으로 응용 프로그램에 대한 전환 처리를 시행.
 이때 다시 ALT+TAB을 눌러 원래의 3D 어플리케이션 복구하려면 오류가 발생된다.
 이 원인은 3D 카드의 설정이 최초  ALT+TAB시 비 Direct3D 응용 프로그램을 위해 설정 변화가 일어났기 때문

② 대책
-> 어플리케이션에서 현재 그래픽 카드의 상태를 확인하고 있다가, 복구시점에서 Device를 Reset하면 된다

③ 복구검사

IDirect3DDevice::TestCooperativeLevel()을 호출하여, 디바이스를 복구 할 수 있는지 검사
-> 반환값(HRESULT)이 D3DERR_DEVICELOST인 경우 디바이스가 소실되었고 아직 복구할 수 없는 상태.
    복구할 수 있을 때 까지 대기한다. 
-> D3DERR_DEVICENOTRESET인 경우 디바이스가 소실되지만 지금 복구할 수 있는 상태이다. 디바이스를 복구한다.


④ 복구절차

-> D3DPOOL_DEFAULT로 잡은 리소스들을 전부 릴리즈한다.
    (안그러면 다음에 호출될 IDirect3DDevice::Reset()이 실패할것이다) 
-> IDirect3DDevice::Reset()을 호출한다.
    (Reset()을 그냥 IDirect3DDevice의 복구 명령어라 생각하면 이해가 편함) 
-> 디바이스를 다시 셋팅한다.
    (랜더스테이트, 뷰 행렬 등..)
-> D3DPOOL_DEFAULT로 잡은 리소스들을 다시 로드한다. 

반응형

'그래픽스(Graphics) > DirectX9~12' 카테고리의 다른 글

점그리기  (0) 2012.11.02
Directx D3DXCreateSphere 구현  (0) 2012.11.02
그래픽카드 디바이스. D3D_Device  (0) 2012.11.02
Directx3D 에서 2D 점 그리기  (0) 2012.11.02
SetScissorRect 영역 그리기  (0) 2012.11.02
반응형

http://cafe.naver.com/mingameworld/1745

 

 

우리가 만약 API로만 프로그래밍을 한다면, 3D는 구현도 어려울 뿐더러, 우리의 게임은 CPU에만 의존하여, 연산을 할것이고,

 

정작 게임을 하기위해 달아놓은(혹은 그렇지 않을수도있지만) 그래픽카드는 편하게 놀게 두는 프로그램이 될것입니다.

 

그래픽연산에는 분명히 CPU보다도 그래픽카드가 더욱 우월함에도 불구하고 말이죠.

 

정확하게 Direct3D만 사용한다고해서, 그래픽카드 GPU의 모든 능력을 사용하는것은 아니지만,

 

최소한 화면에 우리의 게임화면을 그려주는 역할은 그래픽카드에게 맞기는 편이 게임의 보편적 구동성능향상에 도움이 될것입니다.

 

이를위해 우리는 하드웨어인 그래픽카드를 우리가 직접 조작하여야만하며, 하드웨어의 직접적인 조종은 어셈블리언어를 해야만 가능한 일입니다.

 

하지만 MicroSoft사는 개발자의 편의를 위해 DirectX를 만들며, 손쉬운 자체 함수만으로 그래픽카드를 조종할수 있는 함수를 개발해 두었습니다.

 

D3D_SDK를 컴파일러에 포함시킬줄 안다는 전제하에, 사용방법을 간략하게만 설명하겠습니다.(책에 오히려 더 자세히 설명이 되어있으므로,)

 

LPDIRECT3D9 m_pD3D;   //D3D

D3DCAPS9    caps;

m_pD3D = Direct3DCreate9(D3D_SDK_VERSION); //D3D_SDK_VERSION = 현재 사용할 DirectX SDK의 버전을 읽어오는 매크로

 

여기까지 오면 m_pD3D에는 우리의 프로그램에서 D3D를 사용하기 위한 기본적인 초기화가 모두 이루어집니다.

 

m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);

 

여기서 HAL이란,

다양한 종류의 그래픽카드 장치들이 현재 크게 칩셋 제조사로 Nvidia와 Radeon이 있고, 여기서 기계적 레퍼런스를 개조하거나, 또는 다양한

옵션을 덧붙여, 다양한 유통사(기가바이트,아수스,이엠텍, 사파이어 등등)에서 그래픽카드를 내놓습니다.

이들은 모두 기계어로 프로그래밍 되어있으며, 똑같은 기능도 각자의 방법으로 프로그래밍 되어있을것입니다.

이해를 돕기 위한 쉬운예로, 똑같이 화면에 하나의 점을 찍어내는 함수 조차도, 어떤회사는 SetPixel();이라고 지을수 있고, 다른회사는

DrawPixel();이라고 지었을수 있습니다.

이러한 각자 개성이 강하게 프로그래밍 된 소스는 구동시에 어떤 그래픽카드는 게임이 정상적으로 구동되고, 어떤 그래픽카드는 정상적으로

구동되지 않는 문제를 충분히 발생시킬수 있으며, 치명적인 에러를 유발시킬 위험요소가 존재합니다. 따라서 이에 대한 표준(프로토콜/규약)

으로써, 제공할 기능에 대한 표준규약을 MicroSoft에서 제공함으로써, 제조사들은 이 규약만 지켜서 하드웨어를 개발하게 되면,

각 개발사가 그래픽카드를 어떤식으로든 내부 내용을 수정하더라도 DirectX에서는 해당 그래픽카드가 문제없이 구동하게 될수 있게 됩니다.

이 규약이 바로 HAL입니다.

 

따라서 이 함수는 현재 이 프로그램이 구동되고 있는 하드웨어(그래픽카드)의 구성(또는 옵션)정보를 읽어와서, caps라는 구조체 안에

넣어주게됩니다.

그리고, 그 전달받은 값에 대하여 체킹을 합니다.

"버텍스 연산을 사용하는데 있어, 현재 그래픽카드가 지원을 하는지에 대하여"

 if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
 {
  nVP = D3DCREATE_HARDWARE_VERTEXPROCESSING;
 }
 else
 {
  nVP = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
 }
 

만약 지원을 하지않는다면, 소프트웨어적으로 지원을 하지만, 아마 부두급 그래픽카드를 사용하지 않는한 이것은 지원 할것입니다.

 

그리고나선 내가 이제 현재 이 그래픽카드에 대하여, 내가 어떤식으로 사용할지 옵션을 채워넣습니다.

이 인자값은 상당히 많으므로, 함수로써 전달하기에 무리가 있어, 구조체에 모든 데이터를 넣고, 그 구조체 하나를 함수를 통해 넘겨주는

형식으로 옵션을 적용시킵니다. 

 D3DPRESENT_PARAMETERS  d3dpp;
 memset(&d3dpp, 0, sizeof(D3DPRESENT_PARAMETERS));   //선언된 구조체에 대하여 초기화 = zeromemory

 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;   //더블버퍼링(백버퍼)간의 화면 전환을 어떻게 할것인가?
 d3dpp.Windowed  = true;                               //풀스크린시 펄스를 두기도함, 하지만 무조건 트루
 d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;   //임시로 저장시켜두는 벡버퍼의 구성 포멧 지정, 항상 이렇게..
 d3dpp.EnableAutoDepthStencil = true;              //깊이 버퍼(Z버퍼)의 사용을 할것인가?
 d3dpp.AutoDepthStencilFormat = D3DFMT_D16;   //깊이의 단계를 어느정도로 세분화할것인가? D16은 16비트로

                                                                 // 대략 약65000가지의 깊이의 경우를 표현할수 있습니다.

                                                                 // 이정도면 항상 충분하므로, D32도 존재하지만 D16정도로 해줍시다.

 

옵션을 모두 지정하였다면, 지정된 대로 그래픽카드 인터페이스 생성한뒤, 앞으로 내가 명령을 내릴 그래픽카드를 부를 이름을 붙여줍니다. 

m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd, 
  nVP, &d3dpp, &m_pd3dDevice)

이렇게하여 성공적으로 그래픽인터페이스가 생성하고 pd3dDevice라고 이름을 붙여줬다면, 

우리는 앞으로 m_pd3dDevice를 통해 그래픽카드에게 명령을 내릴수 있게됩니다.

 

이것이 그래픽카드 인터페이스를 생성하는 전부입니다.

그리고, 이 m_pd3dDevice라는 녀석에게 5장에서 설명한 파이프라인에 해당하는 내용들을 모두 적용시켜줘야만,

우리는 우리가 원하는 화면을 얻어낼수 있을것입니다.

 

여기서 사족을 덫붙여 한가지 팁을 드린다면,

이 그래픽카드 인터페이스 디바이스는 내 프로그램 내에 하나만 존재하면 된다는 것입니다.

그래픽카드가 두개를 각자 사용하여, 게임을 구동시켜야 할 이유도 없을 뿐더러,

(크로스파이어같은 기술은 2개의 그래픽카드를 하나의 그래픽카드로 인식시켜 연산을 나누는 행위이므로 해당하지 않음)

하나뿐인 그래픽카드의 셋팅을 여러번 받아올 필요가 없다는 것이죠.

우리는 이미 "프로그램 구동중 어떠한 경우에도 한개만 필요한 개체를 다루는 방법"에 대해서 알고 있습니다.

바로 싱글턴이죠.

이 디바이스 셋팅 작업을 클래스화 시켜, 싱글턴 구조로 구성해 놓으면, 앞으로 DirectX게임 개발에 있어 훨~씬 수월해 질수있습니다.

화면을 그릴 모든 것들은 m_pd3dDevice 이녀석을 통해서 모두 이루어 지기 때문에 자주 이녀석을 불러야 하거든요.

기본적으로 싱글턴 패턴을 구성하실수 있다는 가정하에 매크로로 아래와 같이 선언해 놓으시면 훨~씬 더 좋습니다.

예시) //자신의 함수,변수명에 따라 달라질수 있으므로 적절히 수정하여 사용하셔야합니다.

#define D3D_DEVICE CD3DDevice::GetInstance()->m_pD3dDevice 

 

이를 통해, 이 클래스의 헤더가 인클루드가 되어있는 어디서든

D3D_DEVICE-> 이렇게하시면,

CD3DDevice::GetInstance()->m_pD3dDevice 클래스 외부에서도 이것과 동일해지므로,

외부에서도 손쉽게 D3D_DEVICE-> 요렇게 손쉽고, 보기도 편한 소스가 만들어 집니다.

반응형
반응형



http://blog.naver.com/kimachine/130104998068

지금도 주말마다 개발하고 있는 게임이 있는데 쉐이더 공부를 하면서 하니까 오래 걸린다.

그림이 점점 좋아 지고는 있으나 직업으로 하지 못하니 시간을 내기 힘들고 주말에만 잠깐씩 할 수 있다.

공부해서 알게된 쉐이더 기법을 하나씩 적용해 나갔다.

 

1. HDR Lighting 기법

노말매핑을 적용한 물체에 HDR(High Dynamic Range) Lighting 을 적용했다. 

반사되는 부분이 빛이 옆으로 번지는 기법 (Blooming)이 사용되었다.

HDR을 하기 위해서는 먼저 16비트 렌더링 타겟에 물체를 렌더링한다.  즉, A16R16G16B16 포멧을 갖는 텍스쳐를 만들어 이것을 렌더 타겟으로 설정한다.  이후 쏘이는 빛의 세기를 1.0을 초과하는 큰 값을 가지게 하여 렌더링한다.  그러면 각 물체의 표면이 어두운 곳은 변함이 없지만 밝은 곳은 1.0을 넘는 밝기를 가지게 된다.  16비트 텍스쳐는 각 픽셀의 밝기값을 훨씬 크게 저장할 수 있다.  하지만 마지막 보여지는 화면은 각 픽셀의 밝기가 1.0까지만 되므로 1.0을 초과하는 밝기를 다른 형태로 표현해 줘야 한다.  그러기 위해 Blooming효과를 준다.  Blooming 효과 말고도 tone mapping이나 다른 방식이 있으나 번쩍이는 효과를 주기로 했다.  이렇게 얻어진 16비트 텍스쳐는 필터링을 거쳐서 밝은 부분만 걸러내는 작업을 통과하게 된다.  그러면 어두운 화면에 밝은 부분이 군데 군데 남게 된다.  이렇게 얻어진 화면을 Gaussian Blur를 한다.  Blur는 번지게 하는 기법을 말하는데 간단히 말하면 주위 근처의 픽셀 밝기값들을 현재의 픽셀값에 더한후 나누어 주는 것을 말한다.  그러면 어두운 화면에 번져있는 부분이 있는 텍스쳐가 얻어지는데 이것을 원래 그림과 합해서 렌더링해주면 된다.

 





 


2. Environment Mapping

  환경매핑(Environment Mapping)은 일반적으로 매끈한 금속성 표면에 주위가 비춰지는 효과를 나타내 주는 기법을 말하나 나는 바다의 반짝이는 효과를 주기 위해 사용되었다.  바다는 전에 골프장에서 썼던 반사맵을 이용해서 그리면 바다 처럼 보이지가 않았다.  그래서 반짝거리는 효과가 주로 나타내는 데 집중했다.  일반적으로 물을 그릴때는 물의 표면의 움직임(파도)을 나타내는 normal map(법선맵)을 가지고 environment map(환경맵)의 좌표값을 알아낸후 텍스쳐로 사용한다.

  일반적인 노말매핑은 노말맵에 들어 있는 값 (예, -0.7, 0.0, 0.7)을 물체에서 그 좌표에 해당하는 표면이 바라보는 방향으로 간주한다.  즉 어떤 물체의 표면에 매핑된 노말맵의 좌표값이 (0.1, 0.2) 라면 이 좌표값에 있는 노말맵의 색깔 (RGB) 값을 3차원 벡터로 바꿔주고 그 벡터가 가리키는 방향이 그 표면이 보고 있는 방향이라고 간주하는 것이다.  만약 그 표면이 광원을 향하고 있으면 밝게 그려주고 그렇지 않으면 어둡게 그려주므로써 표면을 픽셀단위로 굴곡을 줄 수 있다.  이것은 결국 로우폴리곤 모델로 하이폴리곤 모델을 나타내 주는 방법이다.  그러나 환경매핑은 같은 노말값을 이용해서 광원에 따라 밝기를 계산하는게 아니고 그 노말값이 가리키는 방향에 있는 환경 (즉, 주변물체)의 표면을 그려준다.  하늘을 예로 들면 노말값이 수직이면 하늘의 정 중앙의 이미지를 텍스쳐링하는 식이다.  그러면 결국 울퉁불퉁한 표면이 주위의 환경을 반사하는 물체가 그려진다.  이때 반사되는 물체들을 일일히 찾아서 그려주는 것이 아니고 환경맵이라는 주변을 찍은 텍스쳐를 이용해서 그려준다.  이때 사용하는 텍스쳐는 큐브맵(Cube map) 인데 박스형태의 이미지로 6개의 면으로 되어 있다.  즉, 상하, 좌우, 앞뒤를 가운데 에서 찍은 이미지로 사방의 환경을 나타낸 것이다. 

  물은 이 방법에서 나아가 표면의 노말값이 시간에 따라 바뀌도록 (즉, 돌려주거나 움직이거나 등) 해야 한다.  그러면 겉에 반사된 이미지가 물결에 따라 출렁거리게 보인다.  아래의 스크린 샷에서 반짝이는 하얀 점들은 환경맵에 있는 광원이 번진 것이다.  광원을 크게 그리면 너무 밝게 보여서 멀리 있는 물처럼 보이지를 않아서 작은 광원이 모여있는 방식으로 그렸다.


[출처] 쉐이더 기법 1|작성자 바람소리


반응형

'그래픽스(Graphics) > Shader' 카테고리의 다른 글

[소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
셰이더 내장함수  (0) 2012.11.02
smoothstep  (0) 2012.11.02
NVIDIA계열 그래픽카드 오버클럭 하기  (0) 2012.10.31
반응형

http://http.developer.nvidia.com/GPUGems3/gpugems3_ch22.html



Chapter 22. Baking Normal Maps on the GPU

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.

22.1 The Traditional Implementation

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.

22.1.1 Projection

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.

22fig01.jpg

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.

22.1.2 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.

22fig02.jpg

Figure 22-2 A Boundary Cage

22.2 Acceleration Structures

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).

22.2.1 The Uniform Grid

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.

22fig03.jpg

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.

22fig04.jpg

Figure 22-4 Both Models and the Grid Overlaid

22.2.2 The 3D Digital Differential Analyzer

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.

22fig05.jpg

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.

22.3 Feeding the GPU

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.

22fig06.jpg

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.

Example 22-1. Helper Functions That Convert Between Grid Coordinates

  1.    float3 CoordCubicToSpatial(float3 cubic)  
  2. {  
  3.   float3 normCubic = cubic * g_invGridCubicSize;  
  4.   return g_gridSpatialMinPoint + (normCubic * g_gridSpatialSize);  
  5. }  
  6. float3 CoordSpatialToCubicNorm(float3 spatial)  
  7. {  
  8.   return (spatial - g_gridSpatialMinPoint) * g_invGridSpatialSize;  
  9. }  

Example 22-2. A Function That Maps a Linear Array Coordinate to a 2D Texture Coordinate

  1.    float2 Coord1Dto2D(float index, float2 invTexSize)  
  2. {  
  3.   float offset = index * invTexSize.x;  
  4.   float x = frac(offset);  
  5.   float y = (offset - x) * invTexSize.y;  
  6.   return float2(x, y);  
  7. }  

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.

22.3.1 Indexing Limitations

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.

22.3.2 Memory and Architectural Limitations

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.

Table 22-1. Memory Usage Statistics for a Sample Reference Mesh

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.

22.4 Implementation

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.

22.4.1 Setup and Preprocessing

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.

Example 22-3. Common Data Structures

  1.    struct Cell  
  2. {  
  3.   float3 cubicNormPos;      // Current cubic position in the grid  
  4.    float3 cubicPos;          // Current spatial position in the grid  
  5.    float triListStartIndex;  // Pointer to the start of the tri-list  
  6. };  
  7. struct AABB  
  8. {  
  9.   float3 center;  
  10.   float3 extents;  
  11. };  
  12. struct Ray  
  13. {  
  14.   float3 origin;  
  15.   float3 dir;  
  16. };  
  17. struct Triangle  
  18. {  
  19.   float3 p0;  
  20.   float3 p1;  
  21.   float3 p2;  
  22. };  
  23. bool GetCellAtPoint(out Cell cell, in float3 spatialPos)  
  24. {  
  25.   // Convert spatial to normalized cubic coordinates.  
  26.   cell.cubicNormPos = CoordSpatialToCubicNorm(spatialPos);  
  27.   // Compute unnormalized cubic coordinates.  
  28.   cell.cubicPos = cell.cubicNormPos * g_gridCubicSize;  
  29.   // Fetch the grid volume texture and find the pointer to the  
  30.    // beginning of this cell's triangle list.  
  31.    const float4 coord = float4(cell.cubicNormPos, 0);  
  32.   cell.triListStartIndex = tex3Dlod(gridSampler, coord).r;  
  33.   // Check if we are inside the grid.  
  34.    return (cell.cubicNormPos >= 0) && (cell.cubicNormPos <= 1);  
  35. }  
  36. float3 GetCellMinPoint(in Cell cell)  
  37. {  
  38.   return g_gridSpatialMinPoint + int3(cell.cubicPos) * g_cellSpatialSize;  
  39. }  
  40. float3 GetCellMaxPoint(in Cell cell)  
  41. {  
  42.   return g_gridSpatialMinPoint + (int3(cell.cubicPos) + 1) *  
  43.            g_cellSpatialSize;  
  44. }  
  45. float GetTriangleListIndex(float index)  
  46. {  
  47.   float4 tc = float4(Coord1Dto2D(index, g_invIndexTexSize), 0, 0);  
  48.   return tex2Dlod(indexSampler, tc).x;  
  49. }  
  50. float3 GetVertex(float index)  
  51. {  
  52.   float4 tc = float4(Coord1Dto2D(index, g_invVertexTexSize), 0, 0);  
  53.   return tex2Dlod(vertexSampler, tc).xyz;  
  54. }  
  55. float3 GetNormal(float index)  
  56. {  
  57.   float4 tc = float4(Coord1Dto2D(index, g_invNormalTexSize), 0, 0);  
  58.   return tex2Dlod(normalSampler, tc).xyz;  
  59. }  

22.4.2 The Single-Pass Implementation

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.

Step 1

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.

22fig07.jpg

Figure 22-7 The Cage May Fall Outside the Grid at Some Points

Example 22-4. Setting Up the Ray

  1.    // Set up the ray using interpolated position and cage vector.  
  2. Ray ray;  
  3. ray.dir = -Input.cageDir.xyz;  
  4. ray.origin = Input.position + Input.cageDir.xyz;  
  5. // Check if we are currently outside the grid.  
  6. Cell cell;  
  7. if (GetCellAtPoint(cell, ray.origin))  
  8. {  
  9.   // Compute the grid's min and max points.  
  10.    const float3 gridMin = g_gridSpatialMinPoint;  
  11.   const float3 gridMax = g_gridSpatialMinPoint + g_gridSpatialSize;  
  12.   // Compute intersection between the grid and the ray to find a  
  13.    // starting point within the boundaries of the grid.  
  14.    float t = 0;  
  15.   RayAABBIntersect(ray, gridMin, gridMax, t);  
  16.   // Adjust the ray origin to start inside the grid.  
  17.   ray.origin += ray.dir * t;  
  18.   // Find the first cell to begin traversal.  
  19.   GetCellAtPoint(cell, ray.origin);  
  20. }  

Step 2

The next step is to compute the vectors steptmax, 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 steptmax, and delta.

Example 22-5. Preparing for 3D-DDA

  1.    // Compute tmax  
  2.    float3 cellMin = GetCellMinPoint(cell);  
  3. float3 cellMax = GetCellMaxPoint(cell);  
  4. float3 tmaxNeg = (cellMin - ray.origin) / ray.dir;  
  5. float3 tmaxPos = (cellMax - ray.origin) / ray.dir;  
  6. float3 tmax = (ray.dir < 0) ? tmaxNeg : tmaxPos;  
  7. // Compute traversal steps based on ray direction and cell dimensions.  
  8.    float3 step = (ray.dir < 0) ? float3(-1,-1,-1) : float3(1,1,1);  
  9. float3 tdelta = abs(g_cellSpatialSize / ray.dir);  

Step 3

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.

Example 22-6. The Function Containing the Secondary Loop

This function checks all triangles in the cell and returns the normal closest to the ray origin (in the cage).

  1.  float3 FindNormalAtCell(in Cell cell, in Ray ray)  
  2.   
  3. // Prepare temporary vars:  
  4.  // - last_indices holds the vertex indices for the winning triangle  
  5.  // - last_ret holds the barycentric coords for the winning normal  
  6.  float3 indices, ret;  
  7. float3 last_indices = 0;  
  8. float3 last_ret = float3(0, 0, FLT_MAX);  
  9. // Get the first vertex index; might be a terminator index (-1).  
  10.  float index = cell.triListStartIndex;  
  11. float nextIndex = GetTriangleIndex(index++);  
  12. // Loop until we find a terminator index.  
  13.  while (nextIndex >= 0)  
  14. {  
  15.   // Get remaining vertex indices.  
  16.   indices.x = nextIndex;  
  17.   indices.y = GetTriangleListIndex(index++);  
  18.   indices.z = GetTriangleListIndex(index++);  
  19.   // Get triangle vertex positions.  
  20.   tri.p0 = GetVertex(indices.x);  
  21.   tri.p1 = GetVertex(indices.y);  
  22.   tri.p2 = GetVertex(indices.z);  
  23.   if (RayTriangleIntersect(ray, tri, ret) > 0)  
  24.   {  
  25.     // Use the current hit if closer to the ray origin.  
  26.  bool closest = (ret.z < last_ret.z);  
  27.     last_indices = closest ? indices : last_indices;  
  28.     last_ret = closest ? ret : last_ret;  
  29.   }  
  30.   // Get next index; might be a terminator index (-1).  
  31.   nextIndex = GetTriangleListIndex(index++);  
  32. }  
  33. // Check if we have a valid normal.  
  34.  float3 normal = 0;  
  35. if (last_indices.x * last_indices.y > 0)  
  36. {  
  37.   // Get normals from the winning triangle.  
  38.  float3 n0 = GetNormal(last_indices.x);  
  39.   float3 n1 = GetNormal(last_indices.y);  
  40.   float3 n2 = GetNormal(last_indices.z);  
  41.   // Interpolate normal using barycentric coordinates.  
  42.  float u = last_ret.x;  
  43.   float v = last_ret.y;  
  44.   float t = 1 - (u + v);  
  45.   normal = n0 * t + n1 * u + n2 * v;  
  46. }  
  47. return normal;  

Example 22-7. The Main Loop in the Pixel Shader

Traversing the grid searching for a valid normal.

  1.    float3 normal = 0;  
  2. float done = 0;  
  3. while (!done)  
  4. {  
  5.   // Find a valid normal in the current cell.  
  6.   normal = FindNormalAtCell(cell, ray);  
  7.   // Advance to the next cell along the ray using 3D-DDA.  
  8.    float3 nextCubicPos = cell.cubicPos;  
  9.   if (tmax.x < tmax.y)  
  10.   {  
  11.     if (tmax.x < tmax.z)  
  12.     {  
  13.       nextCubicPos.x += step.x;  
  14.       tmax.x += tdelta.x;  
  15.     }  
  16.     else  
  17.     {  
  18.       nextCubicPos.z += step.z;  
  19.       tmax.z += tdelta.z;  
  20.     }  
  21.   }  
  22.   else  
  23.   {  
  24.     if (tmax.y < tmax.z)  
  25.     {  
  26.       nextCubicPos.y += step.y;  
  27.       tmax.y += tdelta.y;  
  28.     }  
  29.     else  
  30.     {  
  31.       nextCubicPos.z += step.z;  
  32.       tmax.z += tdelta.z;  
  33.     }  
  34.   }  
  35.   // Get the cell at the current point.  
  36.    bool insideGrid = GetCellAtCubicPos(cell, nextCubicPos);  
  37.   // We are done traversing when:  
  38.    // - We found a valid normal. Success.  
  39.    // - We fell outside the grid. Failure.  
  40.   done = (dot(normal, normal) > 0) || !insideGrid;  
  41. }  

Step 4

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.

Example 22-8. Converting the Normal to Tangent Space

  1.    float3 normalTS;  
  2. normalTS.x = dot(normal, Input.binormal);  
  3. normalTS.y = dot(normal, Input.tangent);  
  4. normalTS.z = dot(normal, Input.normal);  
  5. normalTS = normalize(normalTS);  
  6. return float4(normalTS * 0.5 + 0.5, 1);  

Limitation

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.

Example 22-9. An Example of Using Two Nested Loops to Work Around the Limitation

  1.    // Nested pair for the main loop  
  2.    while (!done)  
  3. {  
  4.   while (!done)  
  5.   {  
  6.     . . .  
  7.     // Nested pair inside the FindNormalAtCell function  
  8.    while (nextIndex >= 0)  
  9.     {  
  10.       while (nextIndex >= 0)  
  11.       {  
  12.         . . .  
  13.       }  
  14.     }  
  15.     . . .  
  16.   }  
  17. }  

22.4.3 The Multipass Implementation

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 first pass would perform the same computations as described in the single-pass implementation (steps 1 and 2). Then we would store packed information about the cell and the ray, and store the variables steptmax, and delta on a render texture to be accessed during the following traversal passes.
  • The second pass and the next N passes would perform a single traversal step along the grid at each pixel. To avoid processing empty areas, this pass could use queries to track the rendered pixels and the stencil buffer.
  • The last pass would convert all the pixels in the normal map to tangent space.

Limitation

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.

Tiled Rendering

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.

22.4.4 Antialiasing

The technique presented in this chapter can be extended to support antialiasing in at least two ways:

  • Expanding the pixel shader to trace more rays, each one with a different path deviation. The results would be used as samples to compute an alias-free normal.
  • Rendering the normal map multiple times and jittering the working model's vertex and cage position each time. The samples would then be weighted and accumulated in the render target. The cost of using this method is linear with the number of passes. If a normal map takes around 1 second to render, a 16-sample antialiased normal map will take about 16 seconds.

22.5 Results

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.

22fig08.jpg

Figure 22-8 The Reference Model

22fig09.jpg

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.

22fig10.jpg

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.

22fig11.jpg

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.

Possible Improvements

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.

22.6 Conclusion

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.

22.7 References

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/.

반응형
반응형

#define  FVF_VERTEX_LINE_2D ( D3DFVF_XYZRHW | D3DFVF_DIFFUSE )

struct VERTEX_LINE_2D 
{
  D3DXVECTOR4 vRHW; 
  D3DCOLOR Color; 
}; 


void CMapEditView::DXG_DrawDot( int x,int y, DWORD color )
{

VERTEX_LINE_2D v;
v.vRHW.x=x;
v.vRHW.y=y;
v.vRHW.z=1;
v.vRHW.w=1;
v.Color=color;
m_pd3dDeviceBack->SetFVF(FVF_VERTEX_LINE_2D);
m_pd3dDeviceBack->DrawPrimitiveUP(D3DPT_POINTLIST, 1, &v, sizeof(VERTEX_LINE_2D));


}

반응형
반응형

http://blog.naver.com/inushaha?Redirect=Log&logNo=10020452097

 

내장 함수 설명
abs(x) 절대값을 구한다(x의 각 성분에 대해).
acos(x)
x의 모든 성분의 아크코사인 값을 구한다.
각 성분들의 값은 [ 1, 1] 사이에 있어야 한다.
all(x) x의 모든 성분들이 0이 아닌지를 테스트한다.
any(x) x의 성분 중 0이 아닌 것이 있는지 테스트한다.
as in(x)
x의 각 성분의 아크사인 값을 구한다.
각 성분들의 값은 [ p/2, p/2] 이내에 있어야 한다.
atan(x)
x의 아크탄젠트 값을 구한다.
반환되는 값은 [ p/2, p/2]의 범위 안에 있다.
atan2(y, x)
y/x의 아크탄젠트 값을 구한다. y와 x의 부호는 [-p, p] 범위 안에 있는
반환 값들의 사분면을 결정하는 데 쓰인다. atan2는 원점을 제외한 모든
다른 점들에 대해서도 잘 정의되어 있는데, 심지어 x가 0이고 y가 0이
아닌 상황에서도 잘 동작한다.
ceil(x) x보다 크거나 같은 정수 값을 반환한다.
clamp(x, min, max) [min, max] 범위를 벗어나지 않게 값을 자른다.
Clip(x)
x의 어떤 성분이라도 0부다 작으면 현재 픽셀 값을 버린다. 이것을
이용하여 x의 각 원소가 평면으로부터의 거리를 나타내게 하면 평면
클립도 구현할 수가 있다. 이것은 texkill 같은 명령어를 만들고자 할 때
사용할 수 있는 내장 함수이다.
cos(x) x의 코사인을 구한다.
cosh(x) x의 하이퍼볼릭 코사인(hyperbolic cos ine)을 구한다.
cross(a , b) a , b 벡터의 외적을 구한다.
D3DCOLORtoUBYTE4(x)
4D 벡터 형태인 x의 각 성분을 몇몇 하드웨어에서 지원하는 UBYTE4
스트림 형태로 바꾼다.
ddx(x) 스크린 공간 기준인 x 좌표의 편 미분계수를 구한다.
ddy(x) 스크린 공간 기준인 y 좌표의 편 미분계수를 구한다.
degrees(x) 라디안 값을 각도 값(0∼360도 사이)으로 바꾼다.
determinant(m) m행렬의 행렬식을 구한다.
distance(a , b) a , b 사이의 거리를 구한다.
dot(a , b) a , b 벡터의 내적을 구한다.
exp(x) e의 x 제곱 값을 구한다.
exp2(a) 2의 x 제곱 값이다(각 원소마다).
faceforward(n, i, ng) n * s ign(dot(i, ng)) 값을 구한다.
floor(x) x보다 같거나 작은 것 중 가장 큰 정수 값을 구한다.

fmod(a , b)
a/b의 부동 소수점형(실수형) 나머지를 구한다. a = i * b + f에서 I는
정수형이고, f는 x와 같은 부호를 가지고 있고, f의 절대값은 b보다는 작다.
frac(x) X의 소숫점 부분을 구한다
frexp(x, out exp)
x의 가수와 지수를 계산한다. frexp는 exp의 결과 매개변수에 저장된
가수와 지수를 계산한다. 만일 x가 0일 때는 가수와 지수의 계산 결과를
0으로 계산한다.
fwidth(x) abs(ddx(x))+abs(ddy(x))를 반환한다.
isfinite(x) x가 유한이면 true, 그렇지 않으면 false를 반환한다.
is inf(x) x가 +INF이거나 INF이면 true를, 그렇지 않으면 false를 반환한다.
isnan(x)
x가 NAN이거나 QNAN이면 true를, 그렇지 않으면 false를 반환한다.
CNAN:Net a numbor-잘못된 실수 값
ldexp(x, exp) * 2ex p 값을 구한다.
len(v) 벡터 길이
length(v) 벡터 v의 길이를 구한다.
lerp(a , b, s)
s가 0이면 a를, 1이면 b를 반환하는 a와 b의 선형 보간 값인 a + s(b
a)의 결과 값을 계산한다.
log(x)
지수가 e 인 x의 로그 값을 구한다. x가 음수면 indefinite 값을 반환한다.
x가 0이면 +INF를 반환한다.
log10(x)
지수가 10인 X의 로그 값을 구한다. x가 음수이면 indefinite를, 0이면
+INF를 돌려준다.
log2(x)
지수가 2인 x의 로그 값을 구한다. x가 음수이면 indefinite를, 0이면
+INF를 돌려준다.
max(a, b) a와 b 중 큰 것을 선택한다.
min(a, b) a와 b 중 작은 것을 선택한다.
modf(x, out ip)
x 값을 x와 같은 부호를 같은 소수부와 정수부로 나눈다. 부호가 있는
소수 부분은 반환되고, 정수부는 out ip 값에 저장된다.
mul(a, b)
a와 b의 행렬 곱셈 연산을 수행한다. a가 벡터이면 행 벡터로 인식된다.
b가 벡터면 열 벡터로 인식된다. a의 열과 b의 행은 반드시 같아야 한다.
결과 값은 a행×b열인 행렬이다.
normalize(v)
v/ length(v)로 정규화된 벡터 v를 구한다.
v의 길이가 0이면 결과는 정의되지 않는다..
pow(x, y) xy 값을 반환한다.
radians(x) x를 각도에서 호도(radian) 값으로 바꾼다.
reflect(i, n)
반사 벡터 v를 돌려준다. I는 입사 광선, n은 표면의 법선 벡터이라면 결과
값은 v = i 2 * dot(i, n) * n이다.

refract(i, n, eta)
굴절 벡터 v를 구한다(입사 광선은 I이고, 표면의 법선은 n이고, eta는 굴절
색인). eta로 주어진 n과 I 사이의 각도가 너무 크다면 refract는 (0, 0,
0)을 반환한다.
round(x) x에 가장 가까운 정수를 찾는다.
rsqrt(x) 1 / sqrt(x)를 구한다.
saturate(x) x의 값을 [0, 1] 사이가 되게 자른다.
sign(x)
x의 부호를 구한다. x < 0이면 -1을 돌려주고, x=0이면 0을, x > 0이면
1을 돌려준다.
sin(x) x의 사인 값을 구한다.
sincos(x, out s , out c)
x의 사인과 코사인 값을 구한다. sin(x)는 출력 매개변수 s에 저장되고,
cos(x)는 출력 매개변수 c에 저장된다.
sinh(x) x의 하이퍼볼릭 사인을 구한다.
smoothstep(min, max, x)
x < min이라면 0을 반환하고, x > max이면 1을 반환한다. [min, max]
사이에서는 0과 1사이의 smooth Hermite 보간 값을 반환한다.
sqrt(x) 제곱근(성분별).
step(a , x) (x = a) ? 1 : 0 값을 반환한다.
tan(x) x의 탄젠트 값을 구한다.
tanh(x) x에 대한 하이퍼볼릭 탄젠트 값을 구한다.
transpose(m)
행렬 m의 전치 행렬을 구한다. 입력 값이 mro w s × mc o lu m n s 차원이면
출력은 mc o lu m n s ×mrow s 차원의 행렬이 될 것이다.

반응형

'그래픽스(Graphics) > Shader' 카테고리의 다른 글

[소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
쉐이더 기법  (0) 2012.11.02
smoothstep  (0) 2012.11.02
NVIDIA계열 그래픽카드 오버클럭 하기  (0) 2012.10.31
반응형

http://http.developer.nvidia.com/GPUGems/gpugems_ch16.html

 

Chapter 16. Real-Time Approximations to Subsurface Scattering

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.

16.1 The Visual Effects of Subsurface Scattering

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.

16.2 Simple Scattering Approximations

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.

fig16-01.jpg

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.

fig16-02a.jpg

Figure 16-2 Applying Wrap Lighting to Spheres

Example 16-1. Excerpt from the Skin Shader Effect Incorporating Wrap Lighting

// 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;
}

16.3 Simulating Absorption Using Depth Maps

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.

fig16-03.jpg

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.

Example 16-2. The Vertex Program for the Depth Pass

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;
}

Example 16-3. The Fragment Program for the Depth Pass

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.

Example 16-4. The Fragment Program Function for Calculating Penetration Depth Using Depth Map

// 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.

16.3.1 Implementation Details

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.

fig16-04.jpg

Figure 16-4 Using a Depth Map to Approximate Scattering

16.3.2 More Sophisticated Scattering Models

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.

16.4 Texture-Space Diffusion

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.

fig16-05a.jpg

Figure 16-5 Unwrapped Head Mesh

Example 16-5. A Vertex Program to Unwrap a Model and Perform Diffuse Lighting

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.

Example 16-6. The Vertex Program for Diffusion Blur

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;
}

Example 16-7. The Fragment Program for Diffusion Blur

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.

fig16-06b.jpg

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.

fig16-07.jpg

Figure 16-7 The Final Model, with Color Map

16.4.1 Possible Future Work

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.

16.5 Conclusion

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!

16.6 References

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

Jensen, Henrik Wann, Stephen R. Marschner, Marc Levoy, and Pat Hanrahan. 2001. "A Practical Model for Subsurface Light Transport." In Proceedings of SIGGRAPH 2001.

Nayar, S. K., and M. Oren. 1995. "Generalization of the Lambertian Model and Implications for Machine Vision." International Journal of Computer Vision 14, pp. 227–251.

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).


Copyright

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) 사용법 설명서입니다. "서브서피스 스캐터링"이란 빛이 오브젝트의 표면에 투과되어 내부로 퍼져나간 후 다른 위치로 빠져나가는 현상을 말합니다.

SSSFace.jpg

UE3에 구현된 서브서피스 스캐터링은, 오브젝트의 표면에 입사된 빛을 표면상의 근처 다른 지점으로 블러링(번지게)하는 효과를, 화면-공간(Screen Space) 에서 구현한 것입니다. 물체 안에서 빛이 산란되며 흡수되는 과정을 본따고자, 이 블러 효과는 입사지점과 출사지점 사이의 월드-공간 거리에 따라 라이팅을 감쇠시킵니다.

이러한 추정법으로 피부같은 느낌을 내기에는 충분하지만, 보이지 않는 표면으로부터 물체에 관통되어 완전히 퍼지는 빛 산란 현상까지 잡아내지는 못합니다.

SSSBreakdown.jpg

SSS 켜기


서브서피스 스캐터링을 사용하려면 DirectX11 모드에서 UE를 실행해야 합니다.

추가로 게임의 engine INI 에서도 서브서피스 스캐터링을 켜 줘야 합니다. [SystemSettings] 그룹 아래 AllowSubsurfaceScattering 부분을 찾아 True 로 설정하십시오.

SSS 머티리얼 파라미터


머티리얼에 서브서피스 스캐터링을 추가하려면, 그 EnableSubsurfaceScattering (서브서피스 스캐터링 켜기) 프로퍼티를 참으로 설정해야 합니다:

EnableSubsurfaceScatteringProperty.png

추가로 SubsurfaceInscatteringColor, SubsurfaceAbsorptionColor, SubsurfaceScatteringRadius 머티리얼 파라미터에도 값을 넣어 줘야 합니다:

MaterialParameters.png

SubsurfaceInscatteringColor

인스캐터링 컬러(내부산란색)은 오브젝트에 들어오는 빛에 변조시킬 색입니다. 표면 내부에 산란되는 색 총량을 조절하는 데 좋습니다.

초록에서 빨강으로 변하는 인스캐터링 컬러 예제는 이렇습니다:

VaryingInscatteringColor.png

SubsurfaceAbsorptionColor

앱섭션 컬러(흡수색)은 빛이 산란 반경에 도달한 이후에 흡수되어 남아있는 색입니다. 입사 지점의 하얀 부분에서 최대 반경 지점의 흡수색까지 지수형으로 감쇠됩니다.

인스캐터링 컬러와는 달리, 앱섭션 컬러는 빛이 오브젝트를 빠져나가는 지점에서만 값을 구합니다.

초록에서 빨강으로 변하는 앱섭션 컬러의 예제는:

VaryingAbsorptionColor.png

SubsurfaceScatteringRadius

스캐터링 래디어스(산란 반경)는 빛이 완전히 흡수될 때까지 이동할 수 있는 최대 거리로, 월드 유닛 단위입니다.

앱섭션 컬러처럼 빛이 오브젝트를 빠져나갈 때만 값을 구합니다.

수평 방향으로 가변적인 산란 반경 예제는:

VaryingScatteringRadius.png

콘텐츠 예제

아래 이미지는 캐릭터의 피부 느낌을 내는 데 사용된 서브서피스 스캐터링의 기본적인 예제입니다. 퐁 디퓨즈에 사용된 디퓨즈 텍스처가 있는데, 거기에 한 색을 곱해 인스캐터링 컬러를 만들어 내고 있습니다. 멋진 피부 효과를 만들어 내기 위한 꼼수라면, 퐁과 서브서피스의 라이팅 균형을 맞추는 것입니다. 서브서피스 효과가 강하면 번들번들한 젤리같은 룩이, 퐁 라이팅 쪽이 강하면 캐릭터가 너무 마르고 거칠어 보일 것입니다.

SSS_Content.jpg


반응형

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

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
반응형

Smoothstep 함수 사용법 by 김윤정

smoothstep(min,max,x) : x가 [min, max] 사이의 값인 경우에 대해서 [0, 1] 사이에서 부드럽게 변하는 
Hermite 보간법을 리턴한다. x가 min보다 작다면 0을 리턴하고, max보다 크다면 1을 리턴한다.



..라고 하는데 뭐야 저게... 
라고 해서 알아보니. 

(그림1)

일단 0에서 1로 가는 Liner 한 그래프가 있다고 치죠. 저걸 거꾸로 하면 단순무식한 렘버트 출력값과 같겠죠 (...) 


(그림2)
그리고 min과 max 값을 정합니다. 뭐 이건 0과 1로 정하면 원래 그림인 (그림 1)과 같지만 여기서는 0.2와 0.8로 정했다고 치죠. 

(그림3)

X 값이 min 값보다 작으면 최종 결과는 0 값이 나오고, X 값이 max보다 크면 1값이 나오는건 쉽지요.

(그림4)

근데 만약 X 값이 min과 max 사이에 있는 값이면? 
비율을 다시 계산해서 값을 내놓는다는 것입니다. 그냥 Liner 하게 계산하면 그림과 같겠지요. 
근데 여기서 Hermite Interpolation (보간법)을 사용한다고 합니다. http://en.wikipedia.org/wiki/Hermite_interpolation
... 이거 뭐야 몰라 무서워... 그래픽쟁이한테 이런 수학 공식 들이밀지 마... (이럴때만 그래픽인척 하는 초파리이자 박쥐) 

뭐 그래서 더 생각 안하고, 그림으로 만들어 봤습니다. -_- 
하프 렘버트 출력물을 
밝기에 따라 3단계로 적용. smoothstep 사용. Liner 하지 않다는 것이 보입니다. 
Warp 대용으로 사용한 최종 결과물. 텍스쳐 셈플러 하나 줄였습니다. 만세. 근데 이게 더 무거우면 어쩌지 ㄷㄷㄷ 



즉 아마도 Smoothstep의 보간법은 이런 결과물을 내놓는 듯 합니다. 

(그림5)

반응형

'그래픽스(Graphics) > Shader' 카테고리의 다른 글

[소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
쉐이더 기법  (0) 2012.11.02
셰이더 내장함수  (0) 2012.11.02
NVIDIA계열 그래픽카드 오버클럭 하기  (0) 2012.10.31
반응형

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)

);

 

 


David Adam : d3dx8: Implement D3DXMatrixTransformation2D.

Alexandre Julliard julliard at winehq.org 
Thu Nov 13 08:51:46 CST 2008


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);
 

반응형

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

GPU 에서 노멀맵 굽기  (0) 2012.11.02
표면 스케터링으로 실시간 근사  (0) 2012.11.02
로컬좌표와 월드 좌표  (0) 2012.11.02
라디오시티  (0) 2012.11.02
에르미트  (0) 2012.11.02
반응형

http://www.microsoft.com/downloads/en/confirmation.aspx?FamilyID=fd044a42-9912-42a3-9a9e-d857199f888e&DisplayLang=en


위 링크 누르면 바로 다운저장박스 뜹니다

반응형
반응형

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]는 실제 각 버텍스들의 월드상의 위치.

즉 '월드좌표'입니다.

실제 그물체가 있어야 할곳으로 이동/회전 시켜준다라는 말은 바로

위에서 했던 작업을 말하는겁니다. ( 로컬좌표->월드좌표 )

프로그램상에선..카메라가 왼쪽으로 움직일때 월드상에 객체들을 

오른쪽으로 재 배치 시킵니다. 뿐만아니라 왼쪽으로 돌면 물체들을

오른쪽으로 돌려놓고...

뭐...개념상 그렇다는거죠...이렇게 이야기하나 저렇게 이야기 하나..

결국 최종적으로 나오는 공식은 마찬가지 입니다.

이해가 되셨기를...

        감자 성수올림...@~

반응형

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

표면 스케터링으로 실시간 근사  (0) 2012.11.02
D3DXMatrixTransformation2D  (0) 2012.11.02
라디오시티  (0) 2012.11.02
에르미트  (0) 2012.11.02
Z-Fighting  (0) 2012.11.02
반응형

Radiosity


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.


Direct Illumination

Direct Illumination is a term that covers the principal lighting methods used by old school rendering engines such as 3D Studio and POV. A scene consists of two types of entity: Objects and Lights. Lights cast light onto Objects, unless there is another Object in the way, in which case a shadow is left behind.

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.

Direct Illumination Problems and Advantages

AdvantagesDisadvantages
Ray Tracing:- Can render both mathematically described objects and polygons
- Allows you to do some cool volumetric effects
- Slow 
- Very sharp shadows and reflections
   
Shadow Volumes:- Can be modified to render soft shadows (very tricky)- Tricky to implement
- Very sharp shadows 
- Polygons only
   
Z-Buffer:- Easy to implement
- Fast (real-time)
- Sharp shadows with aliasing problems

ThumbnailThumbnailThumbnail
The most important thing to consider is that, while these methods can produce hyper-realistic images, they can only do this when given a scene with point light sources, and perfectly shiny or perfectly diffuse objects. Now, unless you are some kind of rich simpleton, your house probably isn't full of perfectly shiny spheres and point light sources. In fact, unless you live in a universe with completely different physics, your house probably contains hardly any super-sharp shadows.

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.


Global Illumination

Global illumination methods try to overcome some of the problems associated with Ray Tracing. While a Ray Tracer tends to simulate light reflecting only once off each diffuse surface, global illumination renderers simulate very many reflections of light around a scene. 
While each object in a Ray Traced scene must be lit by some light source for it to be visible, an object in a Globally Illuminated scene may be lit simply by it's surroundings.
The reason this makes a difference will become clear soon.

Global Illumination Problems and Advantages

Images produced by global illumination methods can look very convincing indeed; in a league of their own, leaving old skool renderers to churn out sad cartoons. But, and it's a big 'but': 'BUT!' they are slower. Just as once you may have left your ray tracer all day, and come back to be thrilled by the image it produced, you will be doing the same here.
AdvantagesDisadvantages
Radiosity:- Very realistic lighting for diffuse surfaces 
- Conceptually simple and easy to implement 
- Easy to optimise with 3D hardware
- Slow 
- Does not handle point sources well 
- nor shiny surfaces 
- Always over complicated and poorly explained in books
   
Monte Carlo Method:- Very, very good results.
- Can simulate pretty well any optical phenomenon
- Slow 
- Slightly difficult
- Requires some cleverness to optimise 
- Always over complicated and poorly explained in books

  

Lighting a simple scene with Direct Lighting

   I 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. 
   Turning up the Ambient Light simply caused the room to appear a uniform grey, except for the uniformly red floor, and light patches.
   Adding a point light source in the middle of the room brought out the details, but the scene doesn't have that bright glow that you expect from a sunlit room.
   Lastly, I turned the background colour to white, to give the appearance of a bright sky.

Ray Traced Room
  

Lighting a simple scene with Global Lighting

   I 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.
Interesting points to note:

  • The entire room is lit and visible, even those surfaces facing away from the sun.
  • Soft shadows.
  • The subtle change in brightness across the wall to the left of the scene.
  • The grey walls, far from being grey, have a certain warmth to them. The ceiling could even be said to be ever so slightly pink.
Radiosity Room


The Workings of a Radiosity Renderer

Clear your mind of anything you know about normal rendering methods. Your previous experiences may simply distract you.

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.

Basic Premises:

    1: There is no difference between light sources and objects.
    2: A surface in the scene is lit by all parts of the scene that are visible to it.

Now that you have the important things in mind. I will take you through the process of performing Radiosity on a scene.


Scene View

A Simple Scene

We 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.

Scene ViewNow, lets choose one of the surfaces in the room, and consider the lighting on it.

Scene ViewAs 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.

Scene ViewTake one of those patches. And imagine you are that patch. What does the world look like from that perspective?

Fisheye View

View from a patch

Placing 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.

Fisheye View

View from a lower patch

Pick 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.

Scene View

Lighting on the Pillar

Having 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.

Scene View

Entire Room Lit: 1st Pass

Repeating 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.

Fisheye View

View from the patch after 1st Pass

Patches 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.

Scene View

Entire Room Lit: 2nd Pass

This 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.

Scene View

Entire Room Lit: 3rd Pass

The 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.

Scene ViewScene View
4th Pass16th Pass


The Algorithm In More Detail: Patches

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.

Radiosity Pseudocode: Level 1

  load scene

  divide each surface into roughly equal sized patches


  initialise_patches:
  for each Patch in the scene
    if this patch is a light then
      patch.emmision = some amount of light
    else
      patch.emmision = black
    end if
    patch.excident = patch.emmision
  end Patch loop
  


  Passes_Loop:

  each patch collects light from the scene
  for each Patch in the scene
    render the scene from the point of view of this patch
    patch.incident = sum of incident light in rendering
  end Patch loop


  calculate excident light from each patch:
  for each Patch in the scene
    I = patch.incident
    R = patch.reflectance
    E = patch.emmision
    patch.excident = (I*R) + E
  end Patch loop

  Have we done enough passes?
    if not then goto Passes_Loop

Explanation of Code

initialise patches:
To begin with, all patches are dark, except those that are emmiting light. So, those patches are initialised with some value of emmision, which would have been specified by the scene. All other patches are given zero emmision (black).

Passes Loop:
The code repeats this loop as many times as is necessary to produce acceptable lighting in the scene. Each time round this loop, the code simulates one more reflection of light in the scene.

each patch collects light from the scene
As I explained earlier in the article, each patch is lit by what it can see around it. This is achieved by simply rendering the scene from the point of view of the patch, and adding up the light it sees. I'll explain this in more detail in the next section.

calculate excident light from each patch:
Having worked out how much light is arriving at each patch, we can now work out how much light is leaving 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.


Implementing Radiosity: Hemicubes

The first thing we'll have to deal with, in implementing radiosity, is to solve the problem of looking at the world from the point of view of each patch. So far in this article I have used a fish-eye view to represent a patch's eye view of the scene, but this isn't easy or practical. There is a much better way, the Hemicube!

HemisphereThe 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.

Inside Hemisphere
Rendering from the centre of the hemisphere
HemicubeThe 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.
Inside Hemicube
Rendering from the centre of the hemicube

Unfolding the Hemicube

Unfolding Hemicube
Imagine unfolding the hemicube. What are you left with? One square image and four rectangular images. The square image in the center is a rendering from the point of view of the patch, looking directly forwards. The other four parts of the hemicube are the views looking 90° up, down, left and right.

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.

Compensating for the hemicube's shape

3 SpheresThis 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.

Hemicube Perspective Compensation MapPixels 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)


Lambert's Cosine Law

Lamberts Cos Law MapAny 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)


The two combined: The Multiplier Map

Multiplier MapNow 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.

  • Sum the total value of all pixels in the Multiplier Map.
  • Divide each pixel by this value.
Now, the value at the centre of the map will be much less than 1.0.


3 Hemicubes


Calculating the Incident Light

This procedure takes a point in the scene (usually a patch), along with a normal vector, and calculates the total ammount of light arriving at that point.

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.

  procedure Calc_Incident_Light(point: P, vector: N)  

    light TotalLight
    hemicube H, R, M
    H = empty
    M = Multiplier Hemicube
    R = empty

    div = sum of pixels in M

    camera C
    C.lens = P

    C.direction = N
    H.front = RenderView(C, N, Full_View)

    C.direction = N rotated 90° down
    H.down = RenderView(C, N, Top_Half)

    C.direction = N rotated 90° up
    H.up = RenderView(C, N, Bottom_Half)

    C.direction = N rotated 90° left
    H.left = RenderView(C, N, Right_Half)

    C.direction = N rotated 90° right
    H.right = RenderView(C, N, Left_Half)

    multiply all pixels in H by corresponding
    pixels in M, storing the results in R

    TotalLight = black

    loop p through each pixel in R
      add p to TotalLight 
    end loop
    
    divide TotalLight by div

    return TotalLight
  end procedure
	
Hemicubes Being Multiplied

Explanation of Variable Types in Pseudocode

light: Used for storing any light value. For example:
  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 structure
camera: for example
  structure camera
    point  lens
    vector direction
  end structure


Increasing the accuracy of the solution

You'll be thinking to yourself, 'damn, this seems like a whole lot of rendering. A very processor intensive way of doing things.' You'd be right of course. Basically you have to render a texture mapped scene many thousands of times.

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.


Red OutlineTake 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



Point Light Sources

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.

Spotlight 05
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.

Spotlight 02


Optimising with 3D Rendering Hardware Graphics Card

One of the good things about Radiosity is that it's quite easy to optimise using any 32-bit 3D rendering hardware. As long as you can make it do straight texture mapping, with no shading, anti-aliasing, or mip-mapping, etc.

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.Floating point texture Pointer TextureNow 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.

Rendered Pointers
It should be clear how this optimises radiosity calculations. If it's not obvious, let me know and I'll try and add some more explanation.



Misunderstanding and Confusion:

(What to do with the image once you've rendered it)

The output of a radiosity renderer is an image where each pixel consists of three floating point values, one for each of red, green and blue. The range of brightness values in this image may well be vast. As I have said before, the brightness of the sky is very much greater than the brightness of an average surface indoors. And the sun is thousands of times brighter than that. What do you do with such an image?

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.

Human Vision

Our vision is just about the most important sense we have. Every day I trust my life to it, and so far it hasn't got me killed. Frequently it has saved my life and limb. This was an important sense for our ancestors too, right back to the very first fish or whatever we evolved from. Our eyeballs have had a long time to evolve and have been critical in our survival, and so they have become very good indeed. They are sensitive to very low light levels (the dimmest flash you can see is as dim as 5 photons), and yet can cope with looking at the very bright sky. Our eyeballs are not the only parts of our vision, perhaps even more important is the brain behind them. An increadibly sophisticated piece circuitry, poorly understood, and consisting of many layers of processing takes the output of our eyeballs and converts it to a sense of what actually exists infront of us. The brain has to be able to recognise the same objects no matter how they are lit, and actually does an amazing job of compensating for the different types of lighting we encounter. We don't even notice a difference when we walk from the outdoors lit by a bright blue sky, to the indoors lit by dim yellow lightbulbs. If you've ever tried to take photos in these two conditions, you may have had to change to a different type of film to stop your pictures coming out yellow and dark.

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.


Return to the Good Looking
Textured Light Sourced
Bouncy Fun Smart and Stretchy Page.
Valid HTML 4.01!

반응형

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

D3DXMatrixTransformation2D  (0) 2012.11.02
로컬좌표와 월드 좌표  (0) 2012.11.02
에르미트  (0) 2012.11.02
Z-Fighting  (0) 2012.11.02
직교투영 확대 축소  (0) 2012.11.02
반응형

릴리즈모드 사용 라이브러리 
dxguid.lib 
dxerr9.lib 
d3d9.lib 
d3dx9.lib 
version.lib 
winmm.lib 
comctl32.lib

 

디버그모드 사용 라이브러리 
dxguid.lib 
dxerr9.lib 
d3d9.lib 
d3dx9d.lib   , sdk 에 따라 d3dx9dt.lib 일 수 있음
winmm.lib 
version.lib 
comctl32.lib

반응형
반응형

IDirect3DDevice9::SetScissorRect
Sets the scissor rectangle.

HRESULT SetScissorRect(
  CONST RECT * pRect
);
Parameters
pRect
[in] Pointer to a RECT structure that defines the rendering area within the render target if scissor test is enabled. This parameter may not be NULL.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.

Remarks
The scissor rectangle is used as a rectangular clipping region.

See Rectangles for further information on the use of rectangles in DirectX.

Requirements
Header: Declared in D3d9.h.

 

 

void Render()

{

    Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
    Device->BeginScene();

 

    // 영역을 지정 한다.
    RECT rect;


    rect.left = 0;
    rect.top = 0;
    rect.right = 400;
    rect.bottom = 400;

 

    // 위에서 영역을 지정 했으면 아래 붉은색의 코드를 넣으면 지정한 영역에만 랜더링 된다.

    Device->SetRenderState( D3DRS_SCISSORTESTENABLE, TRUE );
    Device->SetScissorRect( &rect );

 

    // 아래 for 문은 x 파일을 랜더링 하는 코드이다.

    for( int i = 0; i < Mtrls.size(); i++ )
    {
       Device->SetMaterial( &Mtrls[i] );
       Device->SetTexture( 0, Textures[i] );
       Mesh->DrawSubset( i );
    }

 

    Device->SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE );

 

    Device->EndScene();

}

반응형
반응형

 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;
        }
    }


반응형

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

로컬좌표와 월드 좌표  (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 적용된 투영행렬 저장


float g_fNearClip =  1.0f;       // 투영 행렬 제작 시 사용될 기본 NearClip값
float g_fFarClip = 100.0f;       // 투영 행렬 제작 시 사용될 기본 FarVisuality값
 
float g_fNearClipBias =  0.0f; //투영을 살짝 변경 시킬 때 사용될 NearClip값
float g_fFarClipBias =  0.5f;   //투영을 살짝 변경 시킬 때 사용될 FarVisuality값

// 투영 행렬은 렌더링 함수 외부에서 계산을 수행할 때 가장 혼동이 적다.
//살짝 변경될 투영을 만든다.
D3DXMatrixPerspectiveFovLH( &mZBiasedProjMat, D3DX_PI/4,(mProjMat._22/mProjMat._11),  
g_fNearClip + g_fNearClipBias, 
g_fFarClip + g_fFarClipBias  );


// 원래 투영 행렬 로드시킴
m_pd3dDevice ->SetTransform( D3DTS_PROJECTION, & mProjectionMat);

뒤에 그려질 오브젝트를 그려주자.

// 살짝 변경된 투영을 셋트시킨다.
m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mZBiasedProjectionMat);

앞에 그려질 오브젝트를 그려주자.

// 원래 투영 로드됨...
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, & mProjectionMat);

 

결국 두 오브젝트는 겹쳐 있으면서도 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

반응형

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

라디오시티  (0) 2012.11.02
에르미트  (0) 2012.11.02
직교투영 확대 축소  (0) 2012.11.02
폴리곤중간의 텍스처 좌표 구하기  (0) 2012.11.02
[D3DX] Vertex Normal  (0) 2012.11.02
반응형

//뷰의 위치로 잡는다
   GetCursorPos(&Pt);
   pMainView->ScreenToClient(&Pt);          //pMainView 뷰클래스
   

반응형

+ Recent posts