아래에서 퍼옴

http://allosha.tistory.com/category/니시카와%20젠지/범프매핑의%20개량형




시차매핑(Parallax Mapping)

2008-05-16 18:30:00
法線マップを活用したバンプマッピングは、1ポリゴンで表現するにはコストパフォーマンス的に見合わない微細凹凸を表現するために非常に重宝する技術であった。実際、プログラマブルシェーダ・アーキテクチャ・ベー ...... >> Read more

(C) Mainichi Communications Inc. All rights reserved.



법선맵을 활용한 범프매핑은, 하나의 폴리곤으로 표현하려면 코스트 퍼포먼스적으로 맞지 않는 미세한 요철(凹凸)을 표현하는데는 매우 유용한 기술이었다. 실제로, 프로그래머블 셰이더 아키텍쳐 기반의 GPU가 된 이후에, 3D게임 그래픽스에 가장 많이 보급된 기술이라고 할 수 있을지도 모른다.

그런데 , 이 기술도 만능은 아니고, 특정의 조건에서는 한계가 있다. 이것을 개선한 개량버전의 범프매핑이라 할 수 있는 것이 「시차 차폐 매핑」(Parallax Occlusion Mapping)이다.


법선맵에 의한 범프매핑의 약점

법선맵을 활용하는 범프매핑은, 미세요철의 법선벡터를 이용해 실시간으로 광원에 대해서 픽셀단위로 음영 계산을 하기 때문에, 그 입체적인 음영은 광원이 이동하면 제대로 움직이고, 멀리서 보기에는 정말로 미세요철이 있는 것처럼 보인다. 그러나, 실제로는 요철의 지오메트리 정보가 없기 때문에, 시점을 이 요철에 너무 가까이가져가면 거짓말이 드러난다.

3인칭 시점의 3D게임이면 어느 정도, 시점(카메라)을 대상물로부터 떨어 뜨려 놓을 수 있지만, 1인칭 시점의 게임에서는 좀처럼 그렇게도 되지 않는다.

「 F.E.A.R. 」(MONOLITH,2005)으로 부터. 멀리서 보기에는 요철이 제대로 있는 것처럼 보인 벽돌의 벽도 가까이 가서 보면 이와 같다. 실제로 요철은 없고, 평평한데 음영이 붙어 있는 부자연스러움이 드러난다.
(C)2005 Monolith Productions, Inc. All rights reserved. Published by Vivendi Universal Games, Inc. under license from Monolith Productions, Inc. F.E.A.R., Vivendi Universal Games and the Vivendi Universal Games logo are trademarks of Vivendi Universal Games, Inc. Sierra and the Sierra logo are registered trademarks or trademarks of Sierra Entertainment, Inc. in the U.S. and/or other countries. MONOLITH and the Monolith logo are trademarks of Monolith Productions, Inc. All other trademarks are property of their respective owners.


범프매핑에서, 시점이 가까워지면 「 부자연스러움 」이 드러나는 이유는 무엇일까 생각해 보면,

  • 실제로 요철이 있으면, 그 요철에 의한 전후관계의 차폐를 느낄 수 있을테지만, 그것이 없고, 평면에 음영이 붙은 것처럼 보여 버린다

라는 부분에 있다는 것을 알게된다.

이것을 해결해 나가면, 범프 맵핑의 약점을 극복할 수 있는 것이다.


개량형 범프매핑 「시차매핑」

이러한 생각으로, 현재까지 여러가지 개량형 범프매핑이 고안되었다.

SIGGRAPH 2001에서 발표된 「 Polynomial Texture Mapping 」(다항식 텍스쳐 매핑)등이 그 예이지만, 그보다 심플하고, 사전 계산이 필요없고, 구현하기 쉬운 「시차 매핑」(Parallax Mapping) 쪽이 인기를 끌어 그 개량과 보급이 진행되어 왔다.

법선맵을 활용한 범프매핑에서는, 미세요철의 정보로서 그 미세요철의 법선벡터만을 이용하지만, 시차 매핑에서는, 법선벡터에 추가해서 그 미세요철의 높이 정보도 이용한다. 높이정보는, 법선맵을 생성할 때의 중간 정보인 「 높이맵」(Height Map) 에 해당하므로, 귀찮을 것이 없다.

법선맵 기반의 범프매핑의 경우, 어느 픽셀을 그릴 때, 그 픽셀에 대응하는(텍스처 좌표의) 법선맵에서 꺼내 온 법선벡터로, 음영 계산을 한다. 이것은, 곧, 시선과 그 폴리곤 상의 픽셀과의 교차점에 대해서, 실제의 요철량을 무시하고, 음영 계산을 하는 것이다.

그 폴리곤에 대해서 정면으로, 마주 보고 있을 때는  이것만으로도 부자연스러움은 없지만, 시선이 비스듬하게 되면, 이 요철의 음영과 그 표현하고 싶은 요철량과의 엇갈림이 커져서 부자연스럽게 되어 버린다.

거기서, 이 요철량을 고려해 주고자 하는 것이 시차 매핑이다.

법선맵에 의한 범프맵핑의 경우, 요철과 정면으로 마주 보고 있을 때는 부자연스러움은 그렇게 없지만...

 

기울여 보면, 요철의 원근이 고려되지 않아
이런 느낌이 되 버린다.

 원래라면 요철의 원근이 고려되어 이렇게
 보일 것이다. 이것을 하려는 것이 시차 매핑


표현하려고 하는 그 요철이 「매우 완만하다」라고 가정했을 때는, 그릴려는 픽셀의 위치에서의 높이와 시선이 그 요철과 교차하는 곳의 높이는 거의 같다고 할 수 있을 것이다. 그렇다면, 그릴려고 하는 픽셀 위치에서의 높이 정보와 시선이 (서로) 교차하는 위치가, 높이 정보를 고려한 법선맵에서의 참조 위치라고 할 수 있다.

이 "조작"에 의해서, 기울어진 각도의 시선 앞에 있는 요철도 그 나름대로 입체적으로 보이게 된다.

이것이 시차 매핑이다.

보통의 범프 맵핑이 가진 부자연스러움의
원인

시차 매핑은, 법선맵 참조 위치를 요철의 높이를 기록한 높이맵 정보를 바탕으로 재조정하는 버전의 범프 맵핑


< 그림 설명>
좌:
ポリゴン面(폴리곤면), 凹凸の高さ量(요철의 높이량), ハイトマップ(높이맵)
원래 이 시선과 요철(凹凸)이 교차하는 이곳의 법선맵을 참조해야 함
그냥 범프맵핑은 여기의 법선맵을 참조해 버린다.
우:
여기를 시선과 요철의 교차점으로 보고, 이 위치의 법선맵을 참조한다.
원래는 여기 위치의 법선벡터를 참조해야 하지만 오차는 있다.
hxE.xy <-- 높이맵의 높이정보 h를 읽어서 대략적으로 시선 E와 요철이 교차하는 지점을 구한다.


이 시차 매핑을 채용한 3D게임은 이미 몇개정도 존재한다. 픽셀셰이더의 부하는 거의 법선맵 기반의 범프매핑과 다르지 않다는 특성도 그 채용을 적극적으로 만들고 있는 것 같다.(계속)

 「F.E.A.R.」(MONOLITH,2005)로 부터. 왼쪽이 단순한 법선맵 베이스의 범프 맵핑, 오른쪽이 시차 매핑.
(C)2005 Monolith Productions, Inc. All rights reserved. Published by Vivendi Universal Games, Inc. under license from Monolith Productions, Inc. F.E.A.R., Vivendi Universal Games and the Vivendi Universal Games logo are trademarks of Vivendi Universal Games, Inc. Sierra and the Sierra logo are registered trademarks or trademarks of Sierra Entertainment, Inc. in the U.S. and/or other countries. MONOLITH and the Monolith logo are trademarks of Monolith Productions, Inc. All other trademarks are property of their respective owners.

「Tom Clancy's Splinter Cell Chaos Theory」(UBI SOFT,2005)로 부터. 왼쪽이 단순한 법선 맵 기반의 범프 맵핑, 오른쪽이 시차 매핑.
(C)2005 Ubisoft Entertainment. All Rights Reserved. Splinter Cell, Splinter Cell Chaos Theory, Sam Fisher, Ubisoft, and the Ubisoft logo are trademarks of Ubisoft Entertainment in the U.S. and/or other countries. If you need to speak about the game, you have to write the full title this way:Tom Clancy's Splinter Cell Chaos Theory. If you need to speak only about the Splinter Cell licence, you have to name it this way:Tom Clancy's Splinter Cell(R)




시차차폐매핑(Parallax Occlusion Mapping)


범프매핑의 개량형이라고 할 수 있는 「시차매핑」이지만, 서두에서 말한 범프매핑의 약점에 대한 약간의 개선을 보인 것에 지나지 않으며, 부자연스러움은 어느정도는 남아 있다.

예를 들면, 요철의 변화가 매우 심한 경우에서는, 대담한 근사로 구한 요철과, 시선의 교차점과 많이 어긋나 무시할 수 없게 되고, 여전히 요철의 전후관계나 차폐는 무시된 상태 그대로다.

거기서, 다음에 고안된 것이, 이 요철과 시선의 교차점을 구할 때에 정밀도를 좀더 올리려는 접근방법이다. 이것은 픽셀셰이더의 Programmability와 GPU 성능이 매우 향상된 다이렉트X 9 세대 / SM 3.0 대응 GPU가  등장하면서 처음으로 현실성을 띠게 된 기술이다.

이 개량판의 시차 매핑에서는, 세세한 요철의 차폐(Occlusion)관계가 고려되는 것으로 부터 「시차차폐매핑」(Parallax Occlusion Mapping)이라 불린다.

지금까지는 평면에 양각(凸)을 붙인다고 하는 이미지였지만, 시차차폐매핑에서는 평면에 음각(凹)을 조각한다 ……라는 이미지로 구현된다고 하는 것이 표준인 것 같다.

「시선과 요철과의 충돌점을 구하고, 그 위치의 법선맵을 꺼낸다」라고 하는 근본적인 생각은 시차 매핑과 다르지 않다.

문제는 어떻게 요철과 시선의 충돌점을 효율적으로 구해 갈까라는 부분이다.

이것은, 의외로 착실한 실시간 계산으로 실현된다.

구체적으로는, 폴리곤면으로부터 조사점을 그 시선의 연장선상을 따라 조금씩 들어가게 하면서 그때마다, 요철과의 충돌 판정을 실시해 간다. 충돌이라 판정 할 수 있다면, 그 부분에 대응하는 법선맵을 참조한다. 법선맵에서 법선벡터를 가져온 다음의 음영 계산 자체는 범프매핑이나 시차매핑과 같다.

조사점을 시선을 따라 조금씩 들어가게 하고, 그 위치에서의 높이맵을 참조해 그 높이가, 진행된 시선의 높이보다 낮으면, 시선은 계속 진행하게 된다.

계속 조사점을 시선을 따라 진행시켜 나가서, 결국에는 높이맵의 높이가, 그 때의 시선의 높이 보다 높게 된다. 이것은 즉, 요철안으로 조사점이 들어 간 것이 되어, 충돌점은 전회(前回)와 이번 사이에 있다고 판정할 수 있다.「이전과 이번의 중간 지점에 충돌점이 있었다」라고 판단해 거기를 교차점으로 하는 타협을 해도 괜찮고, 혹은 1회 되돌아와서, 거기로부터 시선을 진행시키는 거리를 짧게 해 정확한 충돌지점을 구하려고 해도 괜찮을지도 모른다. (역자: DX 샘플에서는 서로 보간)

어쨌든, 이 기법의 품질을 결정 짓는 것은, 이 조사점을 시선을 따라서, 들어가도록 진행해 갈 때의 스텝의 섬세함(분해능)이다.

렌더링의 결과는, 시차 매핑과는 다르고, 요철과 시선의 차폐를 비교적 정확하게 구해오며, 앞에 위치한 오목(凹) 부분의 틈새로부터 뒤에 위치한 볼록(凸)부분이 보인다 …… 라는 엉켜있는 요철의 전후관계나 차폐관계를 그려낼 수 있다.(계속)


<그림 설명>
視線: 시선、ポリゴン面 : 폴리곤 표면、
ハイトマップの高さ=MAX : 높이맵에서의 최대높이값
 (역자: DX샘플에서는 1로 함)、

ハイトマップの高さ=0 : 높이맵에서의 높이값은 0
(박스) 시선을 그 연장선상을 따라 잠수시켜 간다.

<그림 설명>
(버블) 시선을 여기까지 잠수시켜 그곳의 높이맵을 참조.
(박스) 전진시킨 시선의 높이가 더 크므로 아직 요철과 충돌하지 않았다.


<그림 설명>
(버블) 시선을 좀더 잠수시킨다.
(박스) 시선의 높이가 그곳에서의 높이맵의 높이보다 아래이면 요철과 충돌했다고 판정 할 수 있다.

<그림 설명>
(박스) 이전과 현재 사이의 적당한 지점을 충돌점으로 보고 그곳에 대응하는 법선맵(노말맵)을 참조한다.

                       시차차폐매핑의 동작 개념도



셀프 쉐도우가 추가된 시차차폐매핑(Parallax Occlusion Mapping)

일단, 시선과 요철(凹凸)과의 교차점이 구해지면, 앞에서 기술한 것처럼, 라이팅 자체는 그 교차점에서의 법선맵으로부터 꺼내온 법선벡터를 이용해 계산하면 된다.

이 때, 좀더 생각 해보면, 이 미세 요철에 셀프 그림자(주변의 요철의 그림자)를 생성할 수 있다. 생각은 지금까지의 시선과 요철의 교차점을 구하는 것과 비슷하다.

구해진 시선과 요철의 교차점으로부터, 광원의 방향을 향해서 똑같이 충돌판정을 실시해 주는 것이다.

시선과 요철의 교차점에서 광원 방향으로 조금 진행해서, 그 위치에서의 높이맵을 참조해서 충돌했는지 안했는지를 판정한다. 만약, 한번도 충돌하지 않고 폴리곤 표면을 빠져 나올 수 있으면, 무엇에도 차폐되지 않은 것이 되고, 이곳에는(시선과 요철의 교차점)에는 빛이 닿고 있다고 판정할 수 있다.

반대로, 폴리곤면을 빠져 나오기 전에, 그 외의 요철에 충돌했을 경우는, 다른 요철에 의해서 빛이 차폐되어 있다고 판단할 수 있고,  빛이 오지 않는다고 판정할 수 있다. 즉, 여기(시선과 요철의 교차점)는 그림자라고 판정 할 수 있는 것이다.


<그림설명>
視線(시선)、ポリゴン面 (폴리곤 표면)、
ハイトマップの高さ=MAX (높이맵에서의 최대높이값)
(역자: DX샘플에서는 1로한다)、
ハイトマップの高さ=0 (높이맵에서의 높이값은 0)
(좌)시선과 요철이 교차한 지점
(우)시점을 요철이 교차한 지점에서 광원쪽으로 향하게 해서 충돌을 조사.

<그림 설명>
(좌)시점과 요철이 교차한 지점.
(우)광원방향으로 조사점을 진전시켜 거기에서의 높이맵을 참조. 아직 조사점이 높이맵의 높이 보다 크므로 충동하지 않았다고 간주할 수 잇다.


<그림 설명>
조사점을 좀더 진전시켜서
(우)조사점의 높이 보다 그곳에서의 높이맵의 높이가 더 크기 때문에 조사점은 요철의 속으로 들어갔다... 즉 충돌했다고 판정할 수 있다.
(중)결국 여기는 그림자가 된다고 간주할 수 있다.

            셀프 그림자를 추가한 시차차폐매핑의 동작 개념도

<그림 설명>
(박스) 시점과 요철의 교차점
(버블)광원방향으로 조사해서 아무것도 차폐되어 있지 않으면 빛이 닿고 있는 것이다.(그림자가 아니다)

다만, 이대로는, 그림자이다, 아니다, 라고 너무 딱 잘라 말하는 것이 되어, 그림자의 윤곽이 하드하게 나와 버린다. 경우에 따라서는 깊이그림자 기법(역자: 쉐도우매핑)의 그림자 생성(머지않아 본연재에서 다룰 예정)에서 처럼, 이상한 에일리어싱이 생겨날 가능성도 있다.

거기서, 조사점이 요철에 충돌한 뒤에도 당분간 광원 방향으로 조사를 진행시켜, 「그림자라고 해도, 어느 정도 차폐되어 있는 것인가」를 조사한다.

구체적으로는, 광원으로 돌아와 들어가 있는 이 조사점의 높이와 그 조사점에서의 요철의 높이와의 거리를 구해서 이것을 「 차폐정도 」로서 계측해 나가는 것이다. 적당한 곳에서 조사를 중지해 (이상적인 것은 폴리곤면을 빠져 나올 때까지), 일련의 조사 중에서 가장 큰 차폐상태를 결과값으로 해서, 그림자의 색을 결정해 준다. 차폐 상태가 낮으면 연한색의 그림자, 즉 반그림자(소프트 그림자)로 한다.

이것으로 그림자의 엣지 부근이 부드럽고 흐릿하게 되어, 리얼리티가 증가하게 되는 것이다.


     셀프 그림자를 추가한 시차차폐매핑에서의 소프트 그림자의 실현 방법

<그림 설명>
(박스)차폐 정도를 조사해서 그림자색의 농도에 반영시켜 소프트쉐도우를 실현시킨다.



이 테크닉이 실제의3D게임에서 채용된 예는 아직 적다고 생각되지만, AMD의 Radeon X1000시리즈의 데모 「TOYSHOP」에 채용되어 화제가 되었다.

마이크로소프트의 다이렉트X SDK 데모로 부터. 법선맵에 의한 범프 맵핑

시차차폐매핑

셀프 그림자가 추가된 시차차폐매핑(소프트 그림자 대응)


셀프 그림자가 추가된 시차차폐매핑(소프트 그림자 대응)을 구현한 Radeon X1000시리즈용의 리얼타임 데모  「 TOYSHOP」. 이 입체적인 문자 간판도 셀프 그림자 시차차폐매핑으로 표현되고 있다



 




Posted by 노을삼킨별
,


아래에서 퍼옴

http://allosha.tistory.com/category/니시카와%20젠지/법선맵(노말맵)



2008-04-25 12:00:00
ここまでで、リアルタイム3Dグラフィックスの歴史や概念の部分は大体理解いただけたのではないだろうか。今回からは、実際に3Dゲームなどで応用されている3Dグラフィックス技術の概念などについて細かく紹介していこ ...... >> Read more

(C) Mainichi Communications Inc. All rights reserved.



여기까지해서 리얼타임 3D그래픽스의 역사나 개념 부분은 대강 이해했을 것 같다. 이번회 부터는 실제로 3D게임등에서 응용되고 있는 3D그래픽스 기술의 개념등에 대해 자세히 소개해 나갈 생각이다.

주로, Xbox 360이나 PS3, 2006년 이후의 PC 3D게임 그래픽스등에서 많이 사용되는 테크닉이나 기술을 중심으로 소개해 나가겠다.


법선맵(노말맵) 기술의 대두

2000년, 다이렉트X 8이 발표되고 GPU가 프로그래머블 셰이더 아키텍쳐 기반이 되면서 가장 많이 보급된 기술이 법선맵을 이용한 범프매핑일 것이다.

그래서, 우선 수회에 걸처 「법선맵」의 기술적인 개념에 대한 해설과 실제 게임타이틀에서 응용된 방법들을 소개하겠다.


한개 폴리곤 미만의 미세한 요철(凹凸)을 표현하기 위해

실시간 3D그래픽스는 최종적으로는 적당한 해상도의 2D프레임에 그려진다. 당연하다.

예를 들어 수백만 폴리곤의 고정밀 3D모델이 있다고 해도 이것을 640×480 도트의 화면에서 표시하면, 대부분의 폴리곤이 1 픽셀 미만으로 밖에는 표시되지 않을 것이다.

이것은 극단적인 예이지만, 열심히 세세한 모델링을 해도 실제 표시는 그 모델링 정밀도가 소용 없게  되는 경우가 많다는 것이다. 특히, 그것이 현저한 것이 미세한 요철부분이다.

예를 들면, 인간의 피부상의 주름, 돌층계나 벽돌의 연결부분, 파충류나 어류의 비늘, 무기나 장식품에 새겨진 릴리프 모양 같은 세세한 요철은, 폴리곤으로 모델링을 해도 그것이 실제 표시될 때는 제대로 표시되지 못하는 경우가 많다. 표시되지 못하는 것 뿐이라면 차라리 낫지만, 폴리곤 데이터로서 존재하게 되면, 그것은 메모리를 보다 많이 소비하게 되고, CPU→GPU에의 3D모델의 전송도 불필요하게 시간이 걸려 버린다. 그리고 실제로는 대부분이 한픽셀 이하로 떨어져 버리지만, 그런데도 정점셰이더는 그 미세 요철의 다각형을 모두 처리해야 하기 때문에, 쓸데 없이 고부하를 강요 당하게 된다.

거기서, 발상을 전환해 세세한 요철을 그럴듯 하게만 보이면 된다……라는 페이크(fake) 기법이 모색되기 시작한다. 이것에 대한 하나의 해법이 「법선맵을 이용한 범프맵핑」이다.

이 방법은, 최종적으로 요철이 있는 것 처럼 음영이 나오게 하는 것에 주력하고, 실제로 폴리곤 레벨에서는 세세한 요철을 만들지 않는 것이 특징이다 (대략적인 요철은 만드는 경우가 많다).


법선벡터(노말벡터)만 있으면 음영은 낼 수 있는 것에 착안

미세한 요철이 「요철로 보인다」는 것은 왜일까?

그것은 요철에 밝은 곳이나 어두운 곳이 생기기 때문에 -- 즉 음영이 나오기 때문이다.
그럼 「음영이 나온다」라고 하는 것은 어떤것일까?

이것은 라이팅(광원 처리)이 되기 때문이다.

그럼 라이팅에 필요한 것은 무엇일까?

반사 방정식에는 그 표현하고 싶은 재질에 따라 여러가지가 있지만, 라이팅에 필요한 기본 파라미터는, 시점으로부터의 시선을 나타내는 「시선벡터」, 그리고 물건을 비추는 빛의 방향을 나타내는 「광원벡터」라는 2개의 벡터와 빛에 비추어지는 면(픽셀, 폴리곤)의 방향을 나타내는 벡터인 「 법선벡터」이다.

법선벡터는, 표현하고 싶은 요철에 밀접하게 관련된 파라미터이며, 그 요철의 법선벡터만 있으면, 이것을 사용해 반사방정식을 풀기만 하면 요철 같은 음영을 만들어 낼 수 있다.

다만, 실제로는 요철이 없기 때문에, 그 요철이 실제로 있는지 없는지 판별하기 어려울 정도의 각도나 멀리서 보는 경우가 아니면 부자연스러움이 드러나 버린다. 역으로, 정말로 요철이 있는 것인지 잘 모를 정도의 매우 미세한 요철의 음영 표현에는 적합하다고 할 수 있다.

그러면 미세 요철의 법선 벡터를 어떻게 다루면 좋을까?

최종적으로 미세 요철의 음영은 픽셀을 그린 결과로서 나오기만 하면 되므로, 즉 픽셀 단위로 법선 벡터를 줄려면  어떤것이 적당할지만 생각하면 된다

그럼, 해답은 저절로 나온다.「텍스처」이다.

텍스처라고 하면 일반적으로는 폴리곤에 붙이는 화상이나 모양을 연상하기 쉽다.

예를 들면, 화상텍스처(Diffuse Texture)를 폴리곤에 붙이는 경우는, 텍스처로부터 읽어낸 텍셀(텍스처를 구성하는 화소)의 색을, 지금부터 그릴 픽셀의 색으로 해버린다.
이 텍스처에 미세 요철의 법선벡터를 넣어 두고 텍스처로부터 읽어낸 텍셀을, 법선벡터로서 보고, 그 시점에서 픽셀단위의 라이팅, 즉 반사 방정식을 풀면, 표현하고 싶은 미세한 요철의 음영을 낼 수 있게 된다.

일반적인 텍스처의 텍셀은 화상텍스처의 경우 α(투명도), R(빨강), G(초록), B(파랑)의 최대 네개 요소의 색 정보로 나누어 할당할 수 있는데, 이것을 예를 들어 α을 제외한 RGB의 세 요소에 법선 벡터의 x, y, z성분을 할당해 기록해 둔다. 렌더링 시에 픽셀셰이더에서 이 텍스처로부터 텍셀을 꺼낼 때, 이 RGB 성분을 법선 벡터의 x, y, z 성분으로 간주하고  반사 방정식을 푼다.

이 법선 벡터를 저장한 텍스처를 특별히 「 법선맵 」(Normal Map)이라고 부른다. 일반적으로, 이 법선맵을 이용한 미세 요철 표현은 「법선맵을 이용한 범프매핑」이라고 부르는 것이 맞겠지만, 범프매핑의 기법으로서 법선맵을 활용하는 방법이 표준이 되어 버려서, 범프 맵핑과 「법선맵을 이용한 범프매핑」이 같은 의미로 쓰이고 있다. 또, 텍스처맵을 붙이는 것을 텍스쳐매핑이라고 하는것과 연관지어, 이 기법을 「 법선맵을 붙인다 」는 의미를 담은 「 법선매핑(Normal Mapping) 」이라고 부르는 경우가 많아졌다. (계속)

그림1: 법선 맵을 이용한 범프 맵핑의 개념

<그림 설명>
상:
광원벡터 L, 법선벡터 N, 시선 E, 3D모델 -> 라이팅을 하면 음영이 나온다.
하:
평면
① 비록 실제로는 평면이라도
② 법선벡터의 정보만 있으면...
③ 그것으로 라이팅을 해주면
④ 보기에는 입체적인 음영을 낼 수 있다.




미세한 요철의 높낮이를 색의 농담(濃淡)으로 표현한 높이맵에서 법선맵을 생성

법선맵의 디자인 툴은 무료나 상용 소프트웨어 양쪽에서 다양하게 릴리즈되어 있지만, 상용으로는 Pixologic사의 「ZBrsuh 」가 유명하다. 아울러, ZBrush에서는 하이폴리곤으로 모델링 한 미세요철의 디테일을 로우폴리곤모델 + 법선맵으로 출력하는 기능도 갖추고 있다.

Pixologic사의 「ZBrsuh」에 의한 하이폴리곤 모델로부터의 법선맵 생성

혹은, 「흰색을 높다」 「검정색을 낮다」라고 정의해서, 높이을 표현하는 하이트맵 (높이맵, 디스플레이스먼트맵이라고도 한다)을 준비해 이 흑백의 그레이스케일 텍스처로부터, 법선맵을 생성하는 방법도 자주 사용된다. 높이맵은 흑백의 농담(濃淡)으로 요철(凹凸)을 표현할 수 있으므로, 손으로 미세요철을 그릴 때에는 직관적이고 알기 쉽다. 앞서 말한 ZBrush는, 하이폴리곤모델을 로우폴리곤+높이맵으로 변환하는 기능도 가지고 있다.

높이맵은 미세요철의 높낮이 만을 나타내고 있을 뿐이므로, 법선맵으로의 변환이 필요하게 된다.

이것은 높이맵의 각 텍셀에서의 인접 높낮이값을 구해 가로방향과 세로 방향에 대한 기울기를 계산한다. 각 점에서의 법선벡터는 거기에서의 가로 방향의 기울기, 세로 방향의 기울기와 쌍방으로 직교하는 벡터이므로, 이것을 계산해 준다. 이것을 높이맵의 모든 텍셀 단위로 계산해서, 구한 법선벡터를 텍스처에 출력해 주면 법선맵이 완성된다.

이 높이맵으로부터 법선맵을 변환 생성하는 것은 픽셀셰이더를 활용해서 실시간으로도 가능하지만, 법선맵 자체가 움직이지 않을 경우는, 미리 오프라인으로 사전에 생성해 두기도 한다. 반대로 높이맵을 움직여서, 이것을 리얼타임으로 법선맵으로 변환해 애니메이션하는, 미세요철을 표현하는 독특한 테크닉도 실현 가능하다. 덧붙여서, 머지않아 본 연재에서도 다룰 「수면의 실시간 잔물결 애니메이션」등의 표현은 실제로 이 테크닉을 이용한다.

그림2: 높이맵으로부터 법선맵으로의 변환

<그림 설명>
(좌) 높이맵에서 표현되는 요철(凹凸)의 이미지
(우) 높이맵의 높이로 부터 법선벡터를 구하는 이미지
높이맵

법선매핑(노말매핑)의 실제

법선맵을 이용한 범프매핑의 실제 흐름은 아래의 그림3과 같다.

그림3: 여기에서 가볍게 소개했던 그림을 다시 개재한다.
픽셀쉐이더가 하는일의 예 -- 범프매핑이 완성될 때까지의 개념도. 높이맵으로부터 법선맵으로의 변환도 픽셀셰이더에서 할 수 있다.법선맵은 법선벡터를 저장하는 텍스처로, 텍셀당 XYZ로 나타내지는 3차원 법선벡터 하나가 저장된다.(XY만 저장하고 Z는 계산으로  구하는 방법도 있음)

높이맵을 법선맵으로 변환하고, 픽셀셰이더에서 법선맵을 참조해 법선벡터를 꺼내, 이것을 픽셀단위의 라이팅에 사용한다.

여기서,  한가지 주의해야 할 것은, 법선맵에 있는 법선벡터는, 단지 텍스처라는 평면 위에 구성된 미세요철면의 방향일 뿐, 지금부터 매핑할 폴리곤의 방향을 전혀 고려하지 않고 있는다는 것이다.

법선맵은 단지 2D 텍스처이므로, 매핑할 폴리곤의 좌표계는 전혀 고려되지 않는다. 그래서, 좌표계의 통일을 위한 변환 처리가 필요하게 된다.

그림4

<그림 설명>
(버블) 여기의 법선벡터는 이른바 텍스처좌표계에서의 법선벡터이므로 그대로 폴리곤에 적용할 수 없다.
법선맵(텍스처좌표계),
로컬좌표계,
3D모델


가장 직관적인 것은, 법선맵으로부터 가져온 법선벡터를 월드좌표계나 로컬좌표계로 그때그때마다 변환하는 방법일 것이다. 이 방법은 전픽셀에 대해서, 꺼내온 법선벡터 하나 하나를 좌표계 변환 계산을 해주어야야 하므로 부하가 심하다.

그 때문에, 정점셰이더에서 픽셀셰이더로 받아서 넘겨지는 광원벡터나 시선벡터를, 법선맵을 적용하는 폴리곤 기준의 좌표계와 텍스처 좌표계가 딱 맞도록 변환하는 방법이 일반화되어 있다. 적용하는 법선맵이 고정적인 경우는 법선맵 자체를 사전에 오프라인에서 3D모델의 로컬좌표계로 변환해 두는 방법도 사용된다. 이 부분의 구현방법의 차이는 3D게임 엔진의 설계등에 따라 다르다.(계속)

그림5

<그림 설명>
①일치 시키기 위해서
법선맵,
(버블)좌표계를 법선맵 기준에 맞추어 픽셀단위로 라이팅
법선맵으로 부터 꺼낸 법선 벡터
광원 L,
시선 E,
3D모델,
픽셀셰이더,
정점셰이더,
② 시선 E와 광원 L의 좌표계를 변환



실제의 3D게임에서 보는 법선매핑 활용

법선 매핑은 어떤 장면에 많이 사용될까? 실제의 3D게임에서 보고 가기로 하자.

가장 기본적인 것은, 벽돌이나 암벽과 같은 배경 오브젝트의 재질 표현이다. 전혀 움직이지 않는, 배경 폴리곤에 적용하는 법선맵이면 좌표계는 고정되므로, 계산 부하를 감소 시키기 위해, 법선맵에 넣을 법선벡터의 방향을 미리 로컬좌표계나 월드좌표계로 변환한 상태로 생성해 버리는 것도 좋을지 모른다.

「하프 라이프 2」(VALVE)로 부터. 법선 맵을 Off(왼쪽)한 것과 On( 오른쪽)한 것. 통나무 가죽의 미세 요철에 주목.
(C)2004 Valve Corporation. All rights reserved. Valve, the Valve logo, Half-Life, the Half-Life logo, the Lambda logo, Counter-Strike, the Counter-Strike logo, Source, and the Source logo are trademarks or registered trademarks of Valve Corporation in the United States and/or other countries. All other trademarks are property of their respective owners.


또, 동물이나 괴물의 피부의 미묘한 요철감, 옷의 주름, 자동차나 비행기, 로봇등의 메카닉등의 몰드디테일등도 법선맵의 기본활용 예이다.

캐릭터의 피부등의 질감 표현에 이용하는 경우에는, 비늘이나 가시 같은 복수패턴의 법선맵을 적당하게 스케일링 하면서 혼합해 적용한다는 것도 재미있다. 벡터합 계산은 단순히 대응요소끼리 더하면 되므로 그리 큰 부하는 없다.

「로스트 플라넷」(캡콘)으로 부터.  법선맵 Off (왼쪽)와 On( 오른쪽). 몬스터의 으스스한 피부의 질감도 법선매핑으로 자주 표현된다.
Character Wayne by (C)Lee Byung Hun/BH Entertainment CO., LTD, (C)CAPCOM CO., LTD. 2006, 2007 ALL RIGHTS RESERVED.

「CALL OF DUTY2」(ACTIVISION)으로 부터. 옷의 주름등도 법선맵의 기본적용의 예이다. 캐릭터의 손발이 움직이고, 옷도 신축 하는데, 법선 맵의 옷의 주름만은 변화하지 않는다 …… 라는 것은 부자연스럽게 보이거나 하는 경우도.
(C)2005 Activision Publishing, Inc. Call of Duty is a registered trademarks of Activision Publishing, Inc. All rights reserved.

「CALL OF DUTY2」(ACTIVISION)으로 부터. 법선 맵 Off(왼쪽)와 On( 오른쪽). 메카닉의 몰드 표현도 법선 맵이 활약하는 장소다.
(C)2005 Activision Publishing, Inc. Call of Duty is a registered trademarks of Activision Publishing, Inc. All rights reserved.

「DOOM3」(id software)로 부터. 인간의 얼굴의 주름 표현에 법선매핑을 사용하는 것은 자주 있지만, 이와 같이 얼굴에 상처를 늘려 징그럽게에 변하는 것을 표현한다고 말했었던 것에도 사용할 수 있다.
(C)2004 Id Software, Inc. All rights reserved. Distributed by Activision Publishing, Inc. under license. DOOM and id are registered trademarks of Id Software, Inc. in the U.S. Patent and Trademark Office and in some other countries. Activision is a registered trademark of Activision, Inc. and its affiliates. The ratings icon is a registered trademark of the Interactive Digital Software Association. All other trademarks and trade names are the properties of their respective owners.


법선 매핑은 미세 요철 표현에는 좋다고는 해도, 역시 그 폴리곤에 시점이 너무 가까워지면 , 실제로는 요철이 없는 것을 들켜 버린다.

이것을 개선하기 위해서 독일 CRYTEK사의 3D슈팅게임 「FARCRY」에서는, 독특한 방법을 구현하고 있다.

다른 많은 3D게임이 그렇듯, FARCRY에서도, 시점에서 가까울 때에 표시하기 위한 하이폴리곤으로 구성된 고품위 모델과 시점에서 멀 때 표시하기 위한 로우폴리곤으로 구성된 저해상도 모델을 따로 사용하고 있다. FARCRY에서 독특한 점은, 이, 시점에서 멀어졌을 때의 표시에 이용하는 로우폴리곤모델에 대한 착상이다.

그것은, 로우폴리곤화로 인해 잃은 디테일을 법선맵을 준비해, 로우폴리곤모델에는 이것을 적용해서, 가까울 때나 멀리 있을 때나 동등한 디테일 표현으로 되어 있는 것처럼 보이게 하는 것이다. CRYTEK에서는, 이 테크닉을 「POLYBUMP」기술이라고 명명하고 있다.

어떤 의미에서는, 법선매핑을 LOD(Level of Detail : 시점으로부터의 거리에 따라 적당하게, 표현의 수위를 조작하는 것)시스템에 짜넣은 좋은 예라고 할 수 있다.

  

왼쪽이 약 2800 다각형의 하이 디테일 모델.
폴리곤수를 1/10 의 약 280 개의 로우 디테일 모델로 변환해서, 사라진 디테일을 법선 맵으로 출력한다

   1/10의 다각형수의 모델에 법선 맵을 적용한
   것이 오른쪽의 그림. 시점에서 멀어졌을 때의
   로우폴리곤 모델이 이 레벨로 표시되고
   있으면, 로우디테일 모델로 바뀌었다는 것을
   거의 눈치채지 못할지도 모르다

법선맵으로 대체한 얼굴의 디테일에 주목
(C)2004 Crytek. All Rights Reserved.Published by Ubisoft Entertainment. Far Cry, Ubisoft and the Ubisoft logo are trademarks of Ubisoft Entertainment in the US and/or other countries. Distributed by Kid Station Inc. under license by Ubisoft Entertainment. CryEngine, Polybump technology and Crytek are either trademarks or registered trademarks of Crytek GmbH. All Rights Reserved.


 

Posted by 노을삼킨별
,

아래에서 퍼옴

http://allosha.tistory.com/9

2008-03-24 18:50:00
ここ最近までの3Dグラフィックスの歴史を大ざっぱに理解したところで、今度は3Dグラフィックスの処理の流れを解説したい。 3Dグラフィックスのパイプラインの模式図 3Dグラフィックスの流れを模式化したのが図1だ。 ...... >> Read more

(C) Mainichi Communications Inc. All rights reserved.


최근까지의 3D그래픽스의 역사를 대충 이해했으니, 이번에는 3D그래픽스의 처리 흐름을 해설하고 싶다.

3D그래픽스 파이프라인의 모식도(模式圖)

3D그래픽스의 흐름을 모식화한 것이 아래의 그림1이다. 이것은, 다이렉트X 10 세대 / SM 4.0 대응의 GPU까지의 흐름을 모식화하고 있는데, 일부 흐름의 순서가 GPU에 따라서 다른 경우나 또는 작은 처리단계에 대해서는 일부, 간략화한 부분도 있다. 이점은 양해해 주었으면 좋겠다.

우선, 왜 3D그래픽스 처리가 이렇게 되었는가 하는 근본적인 이야기에 대해서 짚어 보자. 이렇게 된 것은 길고도 짧은 실시간 3D그래픽스의 역사 속에서, 이것이 가장 처리가 매끄럽게 될 것 같고, 그리고 GPU(하드웨어) 의 설계로서도 구현(implementaion)이 (더) 쉽다는 이유 때문이다. 이 흐름은 Direct3D에서도 OpenGL에서도 큰 차이는 없다.

                                       그림1: GPU 내부에서의 렌더링 흐름

<그림 설명>
1: 3D 모델 구축
2: 가상공간에 배치
정점파이프라인
정점셰이더
3: 정점단위의 음영계산
지오메트리셰이더
4: 정점(프리미티브)의 증감(增減)
5: 카메라공간으로의 전개
6: 클리핑이나 음면처리
7: 트라이앵글 셋업과 래스터라이징 처리
픽셀파이프라인
픽셀셰이더
8: 픽셀단위의 음영처리
9: 텍스처 적용
10: 렌더 백엔드(필터, 깊이, 스텐실)(포그, 블렌딩)
11: 출력

CPU가 담당하는 3D그래픽스 처리부분 = 게임엔진!?

그림1에 있는 [1]과 [2] 항목은 주로 CPU에 의해서 수행되는 처리계이다.

3D오브젝트를 배치한다든지, 이동해서 재배치 한다든지....하는 부분에 해당되는 곳으로 이것을 시스템적으로 처리하는 것이 소위 「 게임엔진 」이라고 부르는 부분이다.

게임엔진에서는, 키 입력, 마우스 입력, 게임컨트롤러 입력에 따라서 3D 캐릭터를 이동시킨다든지, 총격이 적에게 명중했는지 안했는지 충돌판정을 한다든지, 충돌의 결과로, 3D 캐릭터를 날려버리기 위한 물리 시뮬레이션을 한다든지 하는데 이런 게임로직 부분은 어떤 의미에서 [1][2]에 해당하는 부분이다.

그리고 [2]는 다이렉트X 10 / SM 4.0 대응의 GPU인 경우 지오메트리셰이더를 활용하면, GPU에서도 가능하게 되어 있다. 예를들면 파티클이나 빌보드 같은 포인트스프라이트에 대해서는 생성이나 소멸을 지오메트리셰이더에 시켜서 CPU를 개입시키지 않고 처리하는 것이 가능하다. 그렇다고 해도 일반적인 3D게임 처리등에서는 아직 이부분은 CPU가 담당하는 부분이라고 할 수 있다.

정점파이프라인과 정점셰이더~ 좌표계란?

그림속에서 빨간라인에 걸쳐있는 [3][4][5][6] 부분은, 정점차원의 처리를 하는 정점파이프라인이다.

보통은 여기부터가 GPU 내부에서 처리가 이루어지는 부분이 된다. 다만, 내부 로직을 간략화해서 낮은 비용으로 그래픽스 기능을 통합시킨, 이른바 「통합칩셋」등에서는, 이 정점파이프라인을 CPU에서 대신하는(emulation하는) 시스템도 존재한다.

좀전까지는 이 정점파이프라인을 「지오메트리 처리」등으로 부르는 경우도 많았다. 지오메트리(Geometry)라는 것은 「기하학」으로 고등학생 이상이면 수학에서 「대수, 기하」등의 수업시간에 「벡터연산」이나 「사영 또는 일차변환」등을 배운 적이 있었을텐데, 이것이 그런 세계의 것이다. 여담이지만, NVIDIA의 GPU인, GeForce 시리즈의 이름의 유래는 「Geometric Force(기하학적인 힘)」을 줄여서 만든 이름이고, 「G-Force(중력)」에 낚였다(끌렸다)는 농담도 존재한다.

이야기로 돌아가서, 3D그래픽스를 말할 때 반드시 등장하는 「삼차원 벡터」라는 개념은 간단히 말하면 「삼차원 공간상의 "방향"」 이라고 생각하면 된다. 그런 "방향"은 x, y, z 3개의 축의 좌표값으로 표시되고, 그 "방향"의 기준을 「좌표계」라고 한다.

이 좌표계에는 로컬좌표계와 월드좌표계(글로벌좌표계)라는 것이 있다.

「로컬좌표계」는, 구체적으로 말하면, 어떤 3D 캐릭터로부터 적당히 결정된 기준이 되는 좌표계이다. 3D의 방향은 그 3D캐릭터의 기준 좌표계에서 「어느쪽을 향하고 있다」고 관리하고 제어하는 방법이 편하다. 그래서 로컬좌표계라는 개념을 사용하는 것이다.

그런데, 일반적인 3D캐릭터에는 팔이나 다리가 붙어 있는 것이 많은데 이것을 그 관절로 부터 회전한다든지 하는 것을 생각하는 경우는 관절을 기준으로 한 로컬좌표계에서 제어하는 편이 쉽다. 그러나 이렇게 생각하면 로컬좌표계는 계층구조가 되버려 최종적으로 처리를 마무리 할 때에는 기준을 알수 없게 된다.

거기서, 그 3D 공간전체를 지배하는 좌표계가 필요해 진다. 그것이 「월드 좌표계」이다. 3D그래픽스의 정점파이프라인의 정점단위 처리에서는 이 로컬좌표계로 부터 월드좌표계로의 변환이 빈번하게 일어난다.

이런 정점단위의 좌표계 변환 처리를 셰이더프로그램에 따라서 실행하는 것이 [3]의 「정점셰이더」(Vertex Shader)인 것이다. 셰이더프로그램을 다시 짜면 유니크하고 특수한 좌표변환을 할 수 있다는 것이다.(계속)

            그림2: 좌표계의 개념도

<그림 설명>
로컬 좌표계,
월드 좌표계



                                          그림1: GPU 내부에서의 렌더링 흐름

정점셰이더가 하는 또하나의 일~ 정점단위의 음영처리

그림1에 있는 [3]의 정점셰이더가 하는일은 좌표변환 뿐만은 아니다. 정점단위의 음영처리/광원처리(라이팅)도 정점셰이더의 중요한 역할이다.

「좌표변환」은 「수학적」인 느낌이 들고, "계산한다", 라고 하는 이미지가  떠올라 알기 쉽다. 하지만, 컴퓨터 안에서 라이팅을 한다.... 즉 "빛을 비춘다." 라고 하는 이미지는 쉽게 연상되지 않을지도 모른다. 물론 GPU는 카메라가 아니고 계산기이므로 실제로 빛을 비쳐 사진을 찍을리 없다. 계산을 해서 이것을 구하는 것이다.

빛이 물체에 닿으면 빛은 거기서 반사/확산되거나 흡수된다. 그 물체에 색깔이나 모양이 있으면 그 색이 보일지도 모르고 비쳐진 빛에 색이 있으면 그 물체의 색이나 모양과 합성된 색이 보일 것이다. 이런 처리를 계산해서 구하는 것이 컴퓨터 그래픽스의 기본적인 방식이다.

이 처리를 어떤식으로 해서 계산기가 잘 계산할까? 이것도 실은 벡터연산을 이용한다.

빛의 방향을 나타내는 「광원벡터」와 시선방향을 나타내는 「시선벡터」, 그리고 빛이 닿는 폴리곤을 구성하고 있는 정점의 방향을 나타내는 「법선벡터」의 3개의 벡터를 사용해 이런저런 벡터의 상대관계로 부터 어느 정도의 빛이 시선방향에 대해 반사하는지를 나타내는 반사방정식을 이용해 계산하는 것이다.

이 반사방정식에는 표현하고 싶은 재질에 따라 다양한 종류들이 있고, 이 반사방정식을 프로그램으로 표현한 것이 정점셰이더 프로그램이다. 그리고 이 정점단위의 반사방정식 프로그램을 실행하는 것도 역시 정점셰이더인 것이다.

정점셰이더에서는 정점단위의 음영처리뿐 아니라 폴리곤에 붙일 텍스처 좌표의 계산도 한다. 텍스처좌표의 계산이라는 것은 어는 폴리곤에 어떤 텍스처를 어떻게 붙여 나갈까라는 대응을 계산하는 것이다. 실제로 텍스처매핑은 [8][9]의 픽셀셰이더가 하고 여기서는 텍스처 매핑을 수행할 준비를 한다라는 이미지이다.

그림3: 정점셰이더가 하는일의 예: 정점셰이더를 활용한
굴절 표현

<그림 설명>
(버블 상)
光源ベクトルL(광원벡터 L)、
法泉ベクトルN(법선벡터 N),視線ベクトルE(시선벡터 E),屈折ベクトルR(굴절벡터 R)、(원)環境マップ(환경맵)
(원) 頂点シェーダ(정점셰이더)
(버블 하)
정점셰이더프로그램
보통의 광원처리를 하고, 나아가 굴절벡터를 구해 면의 건너편의 환경맵을 가리키도록 텍스처 좌표를 수정해 버리자
(우)
건너편이 투명하게 보이는 것 같은 반투명 사과 완성!!

지오메트리셰이더~ 정점의 증감(增減)이 가능한 무시무시한 녀석

다이렉트X 9 / SM 3.0 세대 이전의, 지오메트리셰이더가 없던 세대의 GPU에서는 3D모델의 정점 정보는 CPU쪽 소프트웨어에서 미리 준비해 두는 것이였고, 한번 GPU에 입력되면 이것을 GPU쪽에서 마음대로 증가/감소 시킬수 없었다.

그때까지의 "대원칙의 틀"을 깨고 정점을 자유자재로 증가/감소 시킬수 있는 기능을 가진 셰이더가 [4]의 「 지오메트리셰이더 」이다.

어떻게 증가/감소 시키는가는 지오메트리셰이더를 실행 시키는 셰이더프로그램이 지정한다. 또 실제로 증가/감소 시킬 수 있는 것은 복수의 정점이기 때문에 실질적으로는 선분, 폴리곤, 파티클이라는 각종 프리미티브의 증감이 가능하게 되어 있다.

지오메트리셰이더의 활용방법은 여러가지가 나와 있지만 폴리곤을 자유자재로 생성 가능하기 때문에 지면에 풀(grass)이 되는 폴리곤을 만들게 한다든지 또는 3D 캐릭터의 털(fur)을 만들게 한다든지 하는 것이 가장 기본적인 활용 방침으로 되어있다. 게임등에서는 게임로직과의 인터랙티브(interactive) 처리가 별로 필요 없는 불꽃등의 이펙트 표현을 지오메트리셰이더에서 생성한 파티클로 표현한다... 라는 것도 가능할 것이다.

지오메트리셰이더에서 생성한 정점은 다시 정점셰이더에 돌려보낼 수 있기 때문에 재귀적인 정점처리가 가능하다. 예를들면 (실제로는 보통의 방법으로는 할수 없지만), 로우폴리곤으로 만든 어떤 3D모델로 부터 지오메트리셰이더에서 폴리곤을 보간해서 둥그스름한 하이폴리곤모델을 생성한다... 라는 것도 이론상으로는 가능하다.(계속).

로우폴리곤 모델(좌)로 부터, 산술적으로 폴리곤을 보충해서 하이폴리곤 모델(우)로 변형하는 활용도 생각할 수 있다.

그림4: 지오메트리셰이더가 하는일의 예:
지오메트리셰이더로 털을 생성한다.

<그림 설명>
(원) 지오메트리셰이더
(버블) 털을 표현하는 폴리곤을 심어 버리자




                                       그림1: GPU내부에서의 렌더링 흐름

정점파이프라인의 최종처리

[5][6]은 실제로 그리기 위한 마지막 준비단계적인 처리에 해당된다.

월드좌표계로 변환된 좌표계를, [5]에서는 카메라(시점)에서 잡은 좌표계로 변환한다. 그리고 화면에 표시할 때 어떻게 보일지... 구체적으로는 어떻게 시계(視界, 시야)로 할까하는 변환도 한다. 이것은 사진 촬영의 경우에 카메라의 프레이밍(framing)이나 렌즈의 선택에 해당하는 부분이라 할 수 있다. 이런 일련의 처리를 뭉뚱그려 「투시변환처리」라고도 한다.

어쨋든 3D그래픽스는 시야에 잡힌 영상을 그리기만 하면 되기 때문에, [5]의 처리가 끝나면 시계주체(視界主體, 시야주체)에서 생각하는 방식으로 옮겨 온다 . 

[6]은 그리지 않아도 된다고 판단되는 폴리곤들을, 실제로 그리는 처리를 하는 픽셀파이프라인에 돌입하기 전 단계에서 파기해 나가는 프로세스이다.(컬링처리라고도 한다.)

「클리핑처리」는, 시야로 부터 완전히 벗어난 3D모델의 폴리곤들을 파기하고, 3D모델의 폴리곤 중에 시계에 걸쳐진 폴리곤은 시계 범위내의 폴리곤에서 잘라내는 처리도 한다.

「음면(陰面)처리」는 시점 방향을 향하지 않는, 이론상으로는, 시점으로 부터 보이지 않는 폴리곤을 파기하는 처리이다. 투명오브젝트가 붙어 온 경우에는 이 처리를 하면 이상한 결과가 생기는 경우도 있다.
 

픽셀단위의 테스크로 분해해서 보내는 래스터라이저

시야주체로의 변환도 끝내고, 불필요한 폴리곤도 파기한 후, [7]에서 하는 것은 지금까지 실태(實態)가 없었던 폴리곤을 지금부터 그릴 화면상의 화소(픽셀)에 대응시켜 붙여주는 처리이다. 최신 3D그래픽스에서는 화면에 표시할 프레임을 그리는 것 뿐만 아니라 씬을 텍스처에 렌더링할 경우도 있고, 그럴 경우 [7]에서는 폴리곤과 텍스처화소(픽셀)를 대응시키는 처리를 한다.

이 [7]에서의 처리는 실질적으로는 정점파이프라인에서 정점단위(폴리곤단위)로 출력된 계산결과를 픽셀단위이 일로 분해해서, 이어진 픽셀파이프라인으로 보낸다(발주한다). 말하자면 중개업적인 역할이다.

이 [7]의 처리는 「 트라이앵글 셋업 」, 또는 「  래스터라이즈 처리 」라고 불리고, 틀에 박힌 정해진 처리계이기 때문에, 1990년대의 초기 GPU 때부터 줄곧 고정기능으로 GPU에 들어있고, 지금도 큰 진화는 없다.

통상적으로 1개의 폴리곤은 복수의 픽셀로 그려지기 때문에, 폴리곤은 래스터라이저에 의해서 대량의 픽셀 테스크로 분해된다. GPU에 픽셀셰이더의 개수가 압도적으로 많은 것은 아무래도 픽셀셰이더의 쪽의 일이 (더) 증가해 버리기 때문이다.(계속)

그림5: 래스터라이저는 픽셀셰이더로의 발주서를 작성하는 곳... 이라고도 할 수 있다. 하나의 폴리곤으로부터 복수의 픽셀 테스크가 생겨난다.

<그림 설명>
정점셰이더, 폴리곤, 래스터라이저, 픽셀셰이더 발주서, 픽셀셰이더
(버블) 삼각형->여러개 픽셀



                                       그림1: GPU 내부에서의 렌더링 흐름

픽셀단위의 음영처리를 실행하는 픽셀셰이더~
텍스처는 화상 텍스처만 있는 것이 아니다.

[7]의 「래스터라이즈 처리」에 의해서 생성된 픽셀단위의 음영처리 일을 해내는 것이 [8][9]로 표시된 픽셀셰이더(Pixel Shader)이다. 그리고, 렌더 백엔드(render back-end)까지를 포함한 틀 전체를 「픽셀파이프라인」이라 부른다.
 
GPU에 따라 그 구현된 형태는 여러가지이고, [8]의 픽셀단위의 다양한 음영처리를 수행하는 기능 블럭만을 「픽셀셰이더」라고 부르는 경우도 있으며, 뒤에 기술할 「텍스처 유닛」인 [9]를 총괄해서 픽셀셰이더라고 부르기도 한다.

이제, 그 [8]의 픽셀셰이더에서 수행하는 계산인데, 실은 단위가 픽셀로 되어 있다는 것뿐 수행하는 처리 내용은 정점쉐이더와 닮은 부분이 많다.
 
픽셀단위로 광원벡터, 시선벡터, 그 픽셀의 법선벡터들을 사용해서 반사방정식을 풀고, 그 픽셀이 어떤 색이 될지를 구하는 「픽셀단위의 라이팅」을 하게 된다.

그 경우, 정점단위의 라이팅 결과를 단지 보간해서 그 픽셀의 색으로 하는 간단한 라이팅 보다도 온화한 음영이나 아름다운 하이라이트가 나올수 있다. 이것을 특히 「퍼픽셀라이팅」(Per Pixel Lighting) 이라 부른다.

정점쉐이더에서 구해진 텍스처 좌표로 텍스처에서 텍셀을 읽어 내는 것이 [9]의 텍스처 유닛이다.

이 텍스처 유닛으로 부터 가져온 텍셀의 색과 앞에서 구한 픽셀단위의 음영처리 결과에 의해 구해진 픽셀색 양쪽을 고려해서 최종적인 픽셀색을 구한다.

이 픽셀셰이더에서 실행시키는 셰이더 프로그램이 픽셀셰이더 프로그램이고, 그것에 의해서 픽셀 단위의 라이팅을 특수한 것으로 만들 수 있다.

통상적으로 「텍스처」라고 하면, 폴리곤에 붙이는 화상을 연상하지만, 현재의 프로그래머블 셰이더 시대에서는 그 응용 방법이 확장되어져 왔고,  텍스처에 화상이 아닌 수학적인 (또는 물리적인) 의미를 가진 여러가지 수치 데이터를 넣어 두는 응용들이 생겨났다. 픽셀셰이더에서 픽셀 단위로 음영처리를 할 때는 그 수치 텍스처로 부터 수치데이터를 순차적으로 가져와서 계산에 이용하게 되었다.

텍스처도, PC화면의 화소(픽셀)가 αRGB 각 8비트로 구성되어 있는 것과 동일한 이치로, αRGB의 4개의 요소로 구성되어 있다. 예를 들면 32비트 칼러 텍스처라면 α (투명도) 8비트, R (빨강) 8비트, G (녹색) 8비트, B(파랑) 8비트로 배분되어 있다. 수치 데이터를 텍스처에 넣는 경우, αRGB 4요소에 넣는 것을 생각하면 최대 4개 요소인 벡터나 행렬을 넣어 둘 수 있게 된다. 예를 들어 3차원 벡터이면, 그 X, Y, Z의 3요소의 수치를 αRGB의 RGB에 넣어 둘 수 있다.

실제 픽셀세이더 처리에서는 이 벡터 텍스처로 부터 적당한 텍셀을 꺼내 와서 그것을 벡터 데이터로 해, 시선벡터, 광원벡터, 법선벡터 등과 조합해 특수한 반사방정식을 풀어 독특한 재질을 표현한다.
 
그림6에서는 법선벡터를 텍스처에 넣은 「법선맵」을 사용해 범프맵핑을 하는 예를 보여주고 있다. 이것은 이후의 연재 속에서 한번더 해설 할 것이므로 여기서는 「픽셀셰이더가 하는일이 이런 거다.」라고만 알면 될것 같다. (계속)

그림6: 픽셀셰이더가 하는일의 예 -- 범프맵핑이 완료되기 까지의 개념도.
높이맵에서 법선맵으로의 변환 역시 픽셀셰이더에서 할 수 있다. 법선맵은 법선벡터를 넣어둔 텍스처로 하나의 텍셀에 하나의 XYZ로 표현되는 3차원 법선벡터의 값이 넣어져 있다.(XY만 넣고 Z는 계산해서 구하는 방법도 있음)

<그림 설명>
좌상 ~ 좌하:
높이맵
밝다 = 높다
어둡다 = 낮다
오목과 볼록을 나타내는 텍스처를 작성. 이것이 높이맵
법선맵(노말맵)
어둡다 밝다 어둡다
높이맵의 명암으로 부터 법선벡터를 계산
법선맵의 이미지
법선벡터들이 들어있는 텍스처가 법선맵

우상 ~ 우하:
법선맵으로 부터 꺼내 온 요철(凹凸)의 법선벡터
광원, 시선, 폴리곤
꺼낸 법선벡터로 폴리곤 상의 각 픽셀에 대해 음영계산을 수행한다.
범프매핑의 완성
실제로는 평면이지만 요철이 있는 것처럼 빛을 비추므로 그렇게 보여 버린다.




                                   그림1: GPU 내부에서의 렌더링 흐름

렌더링 최종공정~ 렌더 백엔드

픽셀셰이더의 출력은, 정확히 말하면 「 폴리곤을 구성하는 그 화소가, 그 장면에서는 그 색으로 결정되었습니다. 」라는 것이고, 그대로 비디오 메모리에 써 넣어서 「 한 픽셀의 그리기 완료 」라고 하고 싶지만, 아직 할 일이 있다.

그것이 [10]의 렌더 백엔드 (Render Backend)이다. 덧붙이면 NVIDIA의 경우는 이 부분을 ROP유닛이라고 부르기도 한다. ROP유닛은 Rendering Output Pipeline, 또는 Raster Operation의 약어라는 설이 있지만 확실치는 않다. 본 연재에서는 전자를 정확한 해석이라고 해 두자.

어쨋든, 여기에서는 픽셀셰이더의 출력을 「 써넣어도 좋은것인가의 검증 」, 써 넣을 때는 「 어떻게 써넣을까의 결정 」등의, 비디오 메모리의 쓰기 제어 부분이다. 픽셀셰이더 자신은 텍스처를 읽어 낼 수는 있어도, 비디오 메모리에 써낼 수는 없기 때문에 이 처리는 지극히 중요한 부분이다.
 
그런데, 다이렉트X 9 / SM 2.0 세대 이전의 GPU에서는 픽셀셰이더의 개수와 ROP유닛의 개수가 항상 일치했었기 때문에, 픽셀셰이더와 ROP유닛은 "대(對)" 와 같은 관계를 상상할 수 있었다. 하지만, 다이렉트X 9 / SM 3.0 세대 이후의 GPU에서는 픽셀셰이더 프로그램의 고도화와 함께 픽셀셰이더 개수의 증강이 중점적으로 이루어진 결과, ROP유닛의 개수는 픽셀셰이더의 개수 보다도 적은 것이 일반화 되었다.

최근 GPU에서는 픽셀셰이더 > ROP유닛 구성이 일반적이다.
그림은 GeForce 7800 GTX의 블럭다이어그램.
중간의 4x6 = 24기(基)가 픽셀셰이더. 최하단의 16기(基)가 ROP유닛.

써 넣어도 좋은것인가의 검증」으로써는 「 알파테스트 」「 스텐실테스트 」「 깊이테스트 」라는 것이 있다.

알파테스트는 출력하는 픽셀색이 완전히 투명한가 아닌가의 테스트. α성분이 0으로 투명하다면 그릴 필요가 없으므로 그 픽셀은 그리지 않는다.

그림7: 알파테스트 - 불투명한 부분의 픽셀만 그린다.

<그림 설명>
알파테스트의 예
투명 α = 0
불투명 α = Max
이 픽셀의 그리기는 취소된다


스텐실테스트는 다목적 연산 프레임 버퍼로 이용되는 스텐실버퍼의 내용에 따라서, 어플리케이션이 설정한 조건을 통과할 수 없으면 그 픽셀은 그리지 않는다. 화면의 일부를 도려낸다든지, 스텐실쉐도우볼륨 기법의 그림자 생성 시 그림자 형태를 뽑는 처리등에 응용된다.

그림8: 스텐실테스트 - 스텐실버퍼의 내용을 참조해서, 미리 설정한 테스트 조건을 만족하면 그린다. 이 그림은 「 스텐실버퍼의 내용이 A의 부분만을 씬에 그린다」는 예시이다.

<그림 설명>
(제목) 스텐실테스트의 예
좌 상: 렌더링하는 씬
좌 하: 스텐실 버퍼
우: A 부분은 스텐실테스트를 통과했으므로 그리고, B 부분은 통과되지 않아 취소한다

깊이테스트는 지금부터 그릴 픽셀이 시점에서 봤을 때 가장 앞에 있어서 잘 보이는 픽셀인지 아닌지를 검사하는 것이다. 그리는 픽셀과 1 대 1로 대응하는 Z버퍼라고 불리는 깊이값(Z값)을 넣어두는 버퍼를 미리 준비해 여기서 읽어 낸 깊이값과 지금부터 그릴려는 픽셀의 깊이값을 비교하는 것이 「깊이테스트의 실제 형태」이다. 깊이값은 픽셀셰이더에 의해서 계산되어 나온다.

또한, 반투명 3D 오브젝트를 구성하는 반투명 픽셀을 그리는 경우등은 이 깊이테스트 자체를 하지 않는 경우도 있다.

그림9a: 깊이테스트 - 순서 없이 그릴 때, 깊이테스트는 중요해 진다.

그림9b: Z버퍼에 깊이값을 써넣은 흔적이 없는 곳에는 무조건 써 넣는다. 써넣은 흔적이 있는 곳에서는 깊이테스트를 해서 지금부터 그려내려는 픽셀이 앞이라고 판단할 수 있는 경우만 그린다.

<그림 설명>
그림9a:
상:
(원) 시점
(버블) 본래는 이렇게 보일 것이다
안쪽(깊이값 = 15)
중간(깊이값 = 10)
앞쪽(깊이값 = 3)
하:
고양이 -> 얼굴-> 개 순서로 그렸다고 하면
아무 생각없이 고양이->얼굴->개 이렇게 그리면, 나중에 그린 개가 가장 앞에 그려져 버린다.
이러면 않된다.
그림9b:
프레임버퍼, z 버퍼
상:
중:
A: 여기는 깊이값이 15인 값이 그려진 흔적이 있다. old 15 > new 10 으로 지금부터 그릴 것이 앞이라고 판단. 깊이테스트를 통과하므로 그린다.
B: 여기는 이미 깊이값 3인 값이 그려진 흔적이 있기 때문에 old 3 < new 10으로 앞에 무언가가 있다고 판단.
깊이테스트를 통과하지 못하므로 그리지 않는다.
C: 그려진곳은 Z버퍼를 업데이트해서 다음 깊이테스트에 대비한다.


어떻게 써넣을까의 결정」에 대한 변형(variation)으로는 「 α합성(α블렌딩) 」, 「 포그(안개) 」등이 있다.

α합성은 단지 픽셀을 덮어쓰기해서 그려넣는 것이 아니라, 이미 써넣어진 픽셀색과 반투명합성 계산을 해서 다시 쓰는 처리를 하는 것이다. 렌더링 대상의 프레임버퍼로 부터 픽셀색을 읽어 낸다... 즉 비디오메모리 읽기가 들어가고, 거기에  α합성계산까지도 할 필요가 있기 때문에 의외로 부하가 큰 처리이다. 여담이지만, 3D벤치마크  소프트웨어등에서 반투명 폴리곤 겹쳐쓰기를 연속적으로 한다거나 하는 것은 이 성능을 평가하려는 목적이 있기 때문이다.

그림11: α합성 - α합성에서는 이미 렌더링한 결과를 읽어내 거기에 합성계산을 해서 그려내는 것이기 때문에 부하가 크다.

<그림 설명>
(제목) α합성
(좌) ~ (우)
지금부터 그려 낼 내용 + (프레임버퍼) 이미 그려져 있는 내용 = 합성상태를 나타내는 α값에 따라서 합성

안개(fog)는 지금부터 그릴 픽셀의 깊이값에 따라서 미리 설정해 둔 포그컬러의 섞는상태를 조정하는 처리를 하는 것이다. 안으로 깊이 들어가면 갈수록 그 포그의 픽셀색을 흰색에 가깝게 하는 것과 같은 설정을 하면, 안쪽이 뿌옇게 보이는 공기원근(空氣遠近)의 표현이 가능하다.

그림12: 안개 - 안으로 들어갈수록  픽셀값을 뿌옇게 함으로써 공기의 원근 표현이 가능하다.



물론, α합성도 안개처리도 하지 않는경우는 그 픽셀색을 그대로 비디오메모리에 써내는 처리를 한다. 그리고 써낼때는, 그다음 이후의 다른 픽셀의 깊이테스트에 대비해서, 깊이값을 업데이트해 둔다.

또한, 격자형 화소배열의 화면화소에서의 픽셀이 톱니같은 느낌(jaggy)을 주는 현상을 감소시키는 안티앨리어싱처리도 이 렌더백엔드 부분에서 이루어진다.


Posted by 노을삼킨별
,


아래에서 퍼옴

http://allosha.tistory.com/category/니시카와%20젠지/셰이더%20기술의%20기초지식


2008-01-25 22:20:00
この連載は、最新のパソコンやゲームに用いられている3Dグラフィックス技術を気が向くままに紹介していくものだ。 方針としては、ひとまず、比較的最新のPCゲームや、PS3、Xbox 360などの新世代ゲーム機のゲームで用 ...... >> Read more

(C) Mainichi Communications Inc. All rights reserved.



이 연재는 PC나 게임에 이용되는 최신 3D그래픽스 기술을 마음이 가는대로 소개해 나가는 글이다.

방침은 우선, 비교적 최신의 PC게임이나, PS3, Xbox 360등의 신세대 게임기들의 게임에서 사용된 기술들을 계보를 세워 소개해 나갈 생각이다.

그럼 처음에는 최근 몇 년까지 3D그래픽스 기술이 진화해 온 역사를 돌아 보고 싶다.

실시간 3D그래픽스 기술의 진화 계보


오늘날, 소니 PS3, 마이크로소프트 Xbox 360, 닌텐도 Wii라는 최신 게임기는 물론, 최신의 마이크로소프트 윈도우즈 비스타에서는 유저인터페이스까지 3D그래픽스로 되어 있으며, 닌텐도 DS PSP 휴대게임기,  일부 최신 휴대전화기에도 실시간 3D그래픽스기술이 탑재되어 있다.

대체, 리얼타임 3D그래픽스라는 것은 어떤 진화를 거쳐 어떤 미래로 향하고 있는 것일까? 앞으로 오랫동안 실시간 3D그래픽스와 함께 하려는 사람들을 위해서 우선은 기초지식을 정리해
보자.

1980년대 후반부터 1990년대 전반까지 ~ 플랫셰이딩(Flat shading)에서 텍스쳐매핑(Texture Mapping) 시대로


그 당시까지는 3D
게임에 응용을 안한것은 아니지만, 기본적으로 「 3D그래픽스」는 기술훈련용 시뮬레이터나 자연현상, 광학현상 설명을 위한 학술연구, 또는 영상표현에의 응용등, 프로페셔널한 분야나 연구분야 쪽에서 진화하고 있었다. 1980년대 후반부터 1990년대가 되면서 대형 게임 메이커들이 아케이드 업무용 게임시스템에 실시간 3D그래픽스 시스템을 채용하기 시작하면서 그때부터 게임에 3D그래픽스가 본격적으로 활용되기 시작했다.

이때의 대표적인 작품이라 하면 나무코(Namco)의 「 위닌그란 」(1988), 세가의 「 버추얼레이싱」(1992), 「 버추얼파이터 」(1993)등이 있다. 이때는 폴리곤 모델을 사용했지만 리얼타임으로 표시 가능한 폴리곤 수가 적었고, 라이팅도 폴리곤 단위의 플랫셰이딩이 주류였다.

하지만, 게임과 3D그래픽스의 만남은 「실시간 3D그래픽스」라는 분야를 확립했고 그 진화를 단숨에 가속시켰다.

나무코의 「리지레이서, Ridge Racer」(1993)에서는 텍스쳐매핑이 적용된(화상을 폴리곤에 붙인) 3D 모델이 자유자재로 움직여, 이전의 각각의 쌓여진 나무들 같았던 3D그래픽스로부터 단숨에 리얼리티가 향상되었다.

「 버추얼파이터 」(1993). 폴리곤단위의 라이팅이 기본인  「플랫셰이딩」뿐이였고, 텍스처매핑이라는 개념도 없었다. 그래서 얼굴이나 옷은 "그런 형태의 폴리곤"을 렌더링해서 표현하고 있었다.(C)SEGA

1990년대 중반 ~ 가정용게임기가 문을 연 민간용 실시간 3D그래픽스

특수한 그래픽스 워크스테이션을 제외하고, 「게임플랫폼」이라고 하면, 당시 3D게임그래픽스기술이 가장 앞서 있던 것은 아케이드 게임시스템이였다. 그런데 1994년에는 역사적인 역전극이 일어났다. 그것은 소니 플레이스테이션(PS1)과 세가 새턴(SEGA SATURN)등장이다.

해상도는 그리 높지 않았지만, 텍스쳐매핑까지 대응한 실시간 3D그래픽스가 일반 소비자에게까지 전달 되었다는 점이 의미가 크고, 이것을 계기로 실시간 3D그래픽스와 3D게임그래픽스는 거의 함께 병행하는 관계성을 가지고 급속한 진화를 시작한다.

한편 이 즈음 PC의 일반 유저들을 향한 3D그래픽스기술은, 유감스럽게도 게임기 보다 많이 늦어 있었다. PC에서 일반 유저들을 겨냥한 실시간 3D그래픽스 기술의 본격적인 보급은 1995년에 등장한 새 OS 윈도우즈95의 시대가 되고서 부터이다.

마이크로소프트는 윈도우즈 환경하에서 사운드, 그래픽스, 네트워크, 게임컨트롤러등의 멀티미디어 API 콤포넌트들을 통합한 「 다이렉트X 」를 발표했고, 이것을 윈도우즈95에 채용했다. 다이렉트X는 버전 번호를 뒤에 추가해서 부르는 것이 관례가 되었고, 윈도우즈 환경에서의 본격적인 3D그래픽스 카드가 보급되어 갔다. 실시간 3D그래픽스가 다루어질 수 있게 된 것은 1997년에 릴리즈된 「 다이렉트X 5 」가 되고 부터였다.

정확히 이 타이밍을 전후로 대형 CPU/칩셋 메이커인 인텔은 새로운 그래픽스 인터페이스로써 AGP(Accelerated Graphics Port)  버스의 실용화를 시작했고, 지금도 유력한 그래픽스칩 메이커인 NVIDIA RIVA128 시리즈를, ATI(2006년에 AMD와 합병) RAGE3D시리즈를 출시했고, 이것들은 인기를 얻게 된다. PS1의 등장으로부터 3년 늦게 드디어 PC플랫폼이 PS1과 동등한 실시간3D그래픽스를 다룰 수 있게 된 것이다.

 

                      NVIDIA의「RIVA 128」


  

다이렉트X 5 시대에, 「 PC3D게임을
플레이 한다」는 것을 널리 퍼뜨린
Quake2 」(1997, 아이디소프트)
(C)1997 id Software,Inc.All Rights Reserved.

다이렉트X 6 시대에, 비디오카드의 기본게임으 로써 이름을 널리 알린 「 Incoming 」(1998, Rage)  
   (C)Rage Games Ltd 1998



 

1990년대 후반 ~ 다이렉트X의 급속한 진화의 역사


1990
년대 후반, PC업계가 하나가 되어 강화에 나섬으로써, PC 그래픽스는 급격한 진화를 이뤘다.

1998
년에는 당시까지 발표된 다양한 실시간 3D그래픽스 기능들을 넣은 다이렉트X 6이 발표되었고, 다음해인 1999년에는 그 이후의 PC 그래픽스 진화의 방향을 결정지은 다이렉트X 7도 발표 된다.

다이렉트X 6
이전까지는 3D그래픽스 처리 하드웨어는 폴리곤과 픽셀의 대응을 계산한다든지(래스터라이즈 처리), 화상텍스처를 붙이는 처리를 한다든지…, 하는 픽셀단위의 처리만을 담당하고 있었다.

다이렉트X 7
에서는 그때까지 CPU가 담당했던 정점단위(폴리곤단위)의 좌표변환 처리나 광원처리를
3D
그래픽스 처리 하드웨어가 담당할 수 있는 체계가 구현되었던 것이다. 그래픽스하드웨어로 정점단위의 좌표변환(Transform)과 광원처리(Lighting)을 할 수 있는”  기능을, 특히 이때는「 하드웨어 T&L 」 (T:Transform L:Lighting)이라고 불렀다.

덧붙여,
이 타이밍에서 PC업계는 「 그래픽스처리 전반을 담당하는 프로세서 」의 의미를 헤아려, 이런 하드웨어(프로세서) GPU(Graphics Processing Unit)라고 부르기 시작했고 이후 정착 되었다여담이지만, GPU라는 호칭은 물론, 음운이나 의미는 CPU(Central Processing Unit)를 모방한  것이 다. 다이렉트X 7 대응 GPU로는 NVIDIA의 지포스 256, ATI의 초기 RADEON등이 등장했다.

「NVIDIA GeForce 256」을 탑재한 그래픽스 카드


실시간 3D그래픽스 기술은, 그때까지 가정용 게임기 쪽이 앞서있는 느낌이 강했지만, 다이렉트X 7 시대에 들어서면서 부터는 완전히 PC가 역전한 구도가 되었다. 이것은 가정용 게임기가 보급이 최우선시 되던 전략적인 제약으로 약 5년 싸이클 밖에 하드웨어의 사양 변경을 할 수 없었던 것에 반해서, PC는 최신 기술을 매년 흡수해서 진화할 수 있었기 때문이다. , 당시 게임기의 시장점유율 싸움은 3사 정도의 메이커 사이에서 벌어진 것에 반해, PC그래픽스는 10개사 가까운 메이커들 사이에서 경쟁이 있어났기 때문에 극심한 경쟁원리가 작용했던 것도 적지않은 영향이었을 것이다. 다만, 이어진 다이렉트X 8시대에 돌입할 때까지 큰 도태의 파도가 밀어닥쳐 GPU메이커들도 수개의 회사로 감소한다.


「GIANTS:CITIZEN KABUTO」(2000年、PLANET MOON STUDIOS)。다이렉트X 7시대가 되어, PC의 실시간 3D그래픽스의 표현력은 가정용 게임기를 확실히 앞질렀다.
(C)2000 Planet Moon Studios. All Rights Reserved. Planet Moon and the Planet Moon logo are trademarks of Planet Moon Studios. Giants, Giants: Citizen Kabuto, Interplay, the Interplay logo, and "By Gamers. For Gamers." are trademarks of Interplay Entertainment Corp. All Rights Reserved. Exclusively licensed and distributed by Interplay Entertainment Corp. All other copyrights and trademarks are the property of their respective owners.

 

2000~ 프로그래머블 셰이더 아키텍쳐의 개막.
GPU
메이커들이 도태하는 다이렉트X 8 시대


1998
년에는 세가의 드림캐스트, 2000년에는 대망의 소니의 플레이스테이션2가 발표되지만, 3D 그래픽스의 처리능력에서는 각각의 시점에서의 최신 PC 그래픽스와 비슷했다.

확실히, 선진성은 PC/다이렉트X 쪽에 있었지만, 하드웨어(GPU)의 급격한 진화가 소프트웨어업계를 견인하지 못해, PC쪽은 기술의 동시대성을 얻지 못해왔다.

그렇다고는 해도, PC업계로써는 어떻게든 매년 전세계의 연구자들의 손에 의해 만들어지는 혁신적인 최신 3D 그래픽스 기술을 적극적으로 도입해 가려는 기본방침을 유지해 가고 싶다. 왜냐하면, 최신 기술이 견인해 가는 PC업계로써는 가정용게임기 같은 여유로운 진화 싸이클을 도입하는 것은 어렵기 때문이다.  그렇다고 해서 신기능을 새 GPU에 탑재하고 그때마다 다이렉트X에 그 기능을 활용하기 위한 API를 새로 추가하는 것은 다이렉트X만 증식되어 갈 뿐이다.

, 추가된 신기능을 실제의 어플리케이션에서 (반드시) 이용한다고도 할 수없고, 그런 기능은 다이렉트X 내의 죽은기능(화석기능)으로 계속 남는다. GPU쪽에서도 죽은기능을 위해서 트랜지스터를 잡아 먹는 것은 비용과 전력을 부당하게 잡아 먹어 의미가 없다.

거기서 그래픽스 처리를 소프트웨어 형태로 가능하게 하는 기능(구조)을 GPU에 도입하면 어떨까라는 아이디어가 제창되었다. 그것이 「 세이더를 프로그래밍 가능 」이라고 하는 의미를 담은「 프로그래머블 셰이더 」(Programmable Shader) 라는 개념이다.(계속)
 

   

세계 최초의 민간용 프로그래머블이더 아키텍처를 채용한 GPU GeForce 3



    지포스 3의 사람 얼굴 애니메이션 데모로
    부터. 프로그래머블 셰이더를 활용해서
    구현한 노말맵에 의한 범프매핑 표현이
    옷의 부조(릴리프)나 사람 피부의 주름살에
    적용되어 있다.


 

그런데, 전회(前回)의 마지막에 소개한 프로그래머블 셰이더 아키텍처인데, 이것을 최초로 지원한 다이렉트X가, 2000년 말에 발표된 다이렉트X 8이다.

다이렉트X 8 대응 GPU로는 NVIDIA GeForce3, ATI Radeon 8500등이 있다. 또한 2001년에 발매된 마이크로소프트의 윈도우XP에는, 이 다이렉트X 8이 통합되었다.

프로그래머블 셰이더는 정점처리/폴리곤 단위처리를 담당하는 「 프로그래머블 버텍스 셰이더 」(Programmable Vertex Shader)와 픽셀단위의 음영처리나 텍스처관련 처리를 수행하는 「 프로그래머블 픽셀 셰이더 」(Progammable Pixel Shader) 2개의 타입이 있고, 각각의 셰이더유닛 상에서 다양한 셰이더프로그램을 실행시킴으로써 3D그래픽스 처리를 수행하는 구조로 되어 있다. 또한 용어로써 한마디로 “Programmable Shader”라고 했을 때는 양쪽을 모두 가리키는 경우와 그 개념 전체를 지칭하는 경우가 있다.

그리고, 이 셰이더 프로그램 자체가 소프트웨어이며 개발자가 셰이더프로그램을 작성하는 것으로, 그 GPU에 새로운 그래픽스 기능을 구현하는 것이 가능하다.

이것은 매년마다 발표되는 최신 3D그래픽스 기술을 차세대 GPU의 등장을 기다리지 않고도 그 기술을 실현하는 셰이더프로그램만 개발하면(퍼포먼스의 좋고 나쁨은 별도로하고) 실험이나 실행이 가능하다는 메리트와, 산개된 하드웨어 업체와 소프트웨어 업계에 다시 나아갈 방향을 만들어 주는 계기도 되어 주는 것이였다.

다만, 프로그래머블 셰이더 아키텍처의 실현은 GPU 메이커들에 높은 기술력을 요구했기 때문에, 이후 다이렉트X 9가 등장하는 동안에, 초기의 실시간 3D그래픽스를 지탱해 왔던 GPU 메이커들이 문을 닫는 경우가 많았다. 도태의 결과로, 2001년 이후로는 GPU 메이커의 쌍두마차인 NVIDIA와 ATI의 치열한 GPU 전쟁이 두드러졌다.

현재, 이 2개 회사 외에 PC용 민간타겟의 GPU를 양산 개발을 하고 있는 곳은 인텔(단, 통합칩셉쪽으로)과 S3사 정도이다. PC 그래픽스 여명기를 지탱했던 3dfx사는 2000년 NVIDIA사에 인수되었고, 비슷하게 Number Nine사도 1999년에 도산했다. 윈도우즈 9x 시절에 일본에서는 절대적인 인기를 자랑했던 MATROX사도 최후발 주자로 다이렉트X 8 세대 GPU인 Parhelia시리즈을 투입했지만, 이후에는 최신기술에 대응한 GPU 개발로 부터는 멀어졌다. 프로페셔널한 워크스테이션용 GPU를 개발했던 3Dlabs사도 윈도우즈 9x 시절에 민간용 Permedia시리즈를 투입했지만 성과없이 2002년에 크리에이티브사에 매각되었고, 2006년에는 프로페셔널 지향의 GPU사업에서 은퇴를 발표했다. 칩셋 메이커인 SiS사에서 독립한 GPU 전문 신생 메이커였던 XGI사도 2006년 최신 기술에 대응하는 새로운 GPU 개발에서 사실상 물러났다.

그런데 다이렉트X 8 발표로 부터 약 1년 후인 2001년 말에는 다이렉트X 8 베이스의 게임기인, 초대 「 Xbox 」가 마이크로소프트로 부터 발매되었다. 별로 이점이 화제가 될 만한 것은 아니지만 Xbox는 세계 최초의 프로그래머블 셰이더 아키텍처를 채용한 가정용 게임기가 되었다.


   

초대 Xbox. 지포스3을 베이스로 한 GPU를 탑재했고, API로 다이렉트X를 채용










    Xbox용, PC용 양쪽에 발매된 "Splinter
    Cell" (UBI SOFT)은 프로그래머블 셰이더 기반의 3D게임 그래픽스의 가능성을 세상에 보여주었다.

(C)2002 Ubi Soft Entertainment. All Rights Reserved. Ubi Soft Entertainment and the Ubi Soft logo are registered trademarks of Ubi Soft Entertainment. Splinter Cell is a trademark of Ubi Soft Entertainment. All other trademarks are the property of their respective owners.


 

 

2002/2003 ~ 프로그래머블 셰이더 2.0 1기 다이렉트X 9 시대

다이렉트X 8 시대의 프로그래머블 셰이더는 GPU 메이커 마다 약간의 아류버전이 몇개씩 혼재되었던 관계로 버전 1.x라고 규정된다. 그리고 2002년에는 새로운 프로그래머블 셰이더인 버전 2.0 사양을 지원하는 다이렉트X 9가 발표된다.

프로그래머블 셰이더 사양은 셰이더 모델(Shader Model, SM) 키워드 뒤에 버전 번호를 붙여 아키텍처 세대를 나타내는 것이 이때 부터 일반화 되었다. 즉, 예를 들면, 다이렉트X 9 세대는 「 SM 2.0 」대응 GPU가 대두하는 시대가 되었다....라고 하는 것이다.

SM 2.0에서는, SM 1.x와 비교해서 보다 긴 셰이더 프로그램이 실행 가능하고, 명령어의 종류가 증가했고, 사용할 수 있는 명령어의 조합 제한도 감소 되었다. 또, 지금까지 정점 셰이더만 활용되던 부동소수점 연산이 픽셀셰이더에서도 사용 가능하게 되었고, 픽셀단위의 음영처리의 연산 정밀도와 표현할 수 있는 다이나믹 레인지도 넓어졌다. 이 확장이 High Dynamic Range 렌더링: HDR 렌더링이라고 불리는 그 이후의 실시간 3D 그래픽스에서의 새로운 트렌드를 만드는 계기가 되었다.

다이렉트X 7 / 하드웨어 T&L 시절의 1호 GPU와 다이렉트X / 최초의 프로그래머블 셰이더 대응 GPU가 모두 NVIDIA로 부터 릴리즈 되었던 것에 반해서 다이렉트X 9 / SM 2.0 대응 GPU 1호기는 ATI로 부터 릴리즈 된 Radeon 9700이였던 것도 감개무량한 일이였다. NVIDIA도 이것에 대항해 GeForce FX로 대응했지만 제조 상의 문제와 더불어 퍼포먼스적으로도 고전했고, 이 SM 2.0 시절은 ATI 우세인 채로 지나갔다.

SM 2.0 대응 최초의 GPU는 ATI로 부터 릴리즈된 Radeon 9700이였다. 이후의 9800, 아래의 9600/9500 시리즈도 인기가 많았다.

그리고 SM2.0 시절 ATI 우세를 결정지었던 것은 이때 가장 등장이 기대되었던 VALVE사의 대작게임 「하프라이프2」가 ATI Radeon 9500/9600/9700/9800시리즈를 최적의 GPU로 해서 마케팅전략을 전개했던 일이였다.

「하프라이프2」(VALVE)는 당초 2003년 발매 예정이였지만, 연기에 연기를 거듭해 결국 발매된 것은 2004년말이였다. 그래도 좋든 싫든 하프라이프2는 SM2.0세대 GPU 발전에 기여했다.
(C)2004 Valve Corporation. All rights reserved. Valve, the Valve logo, Half-Life, the Half-Life logo, the Lambda logo, Counter-Strike, the Counter-Strike logo, Source, and the Source logo are trademarks or registered trademarks of Valve Corporation in the United States and/or other countries. All other trademarks are property of their respective owners.

2004/2005/2006년~ 프로그래머블 셰이더 3.0과 2기 다이렉트X 9 시대

SM 2.0은 "2.0"이라고 하면서도 실제는 1.x 때와 똑같이 몇개의 마이너 아류를 낳았다. NVIDIA는 GeForce FX에 프로그래머블 정점셰이더 2.0a 와 프로그래머블 픽셀셰이더 2.0a라는 이름을 붙혀서 지원했다.

다이렉트X 9의 등장부터 약 2년이 경과한 2004년, 마이크로소스트는 다이렉트X 10은 발표하지 않고, 새로운 다이렉트X 9의 마이너 버전업판을 발표해서 프로그래머블 셰이더 사양 3.0, 즉 SM 3.0으로의 대응을 이루어 냈다.

SM 버전이 1.0 올라갔지만 다이렉트X 버전을 올리지 않은 것은 「 뒤에 나올 윈도우즈 비스타에 맞추기 위해서 」「 뒤에 나올 ATI의 SM 3.0 "미"대응 GPU를 배려했기 때문에 」등의 설이 있지만 명확하지는 않다.

SM 3.0에서는 사실상, 셰이더프로그램의 길이 제한이 해제되고, 정점셰이더, 픽셀셰이더 양쪽의 명령어 셋도 확충되었다. SM 2.0에서는 동적 조건분기 반복은 정점셰이더에 제한되어 있었지만, SM 3.0에서는 픽셀셰이더에서도 지원되었고, 사실상,  programmablity 면에 있어서 정점셰이더와 픽셀셰이더의 격차가 없어졌다. 또, 정점셰이더에서도 텍스처를 액세스할 수 있는 새로운 기능 「Vertex Texture Fetching:VTF 」(별명 정점 텍스처링)의 지원도 이때 강하게 어필되었다.

SM 3.0 대응의 최초의 GPU는 NVIDIA로 부터 발표된 GeForce 6800 시리즈이고, 의외였던 건 ATI는 같은 2004년에 등장 시킨 새 GPU, Radeon X800 시리즈에 대해서 SM 2.0 대응에만 머무는 선택을 했다.

NVIDIA GeForce 6800 시리즈. 최초의 SM 3.0 대응 GPU는 다시 NVIDIA로 부터 등장했다.

Radeon X800시리즈는 정점셰이더 2.0a, 픽셀셰이더 2.0b로 확장한 개량판 SM 2.0 대응 GPU가 되었고, 2004년에는 두 쌍두마차의 보조가 맞지않아 유저가 혼란스러웠던 해였다. SM 2.0도 "2.0"이라고 하면서도 이와 같이 ATI 와 NVIDIA가 독자적으로 확장해 버려 작은 버전 번호들이 서로 일치하지 않았다.

2004년은 새로운 버스 인터페이스 「 PCI-Express 」도 제공되기 시작한 해였고, 그래픽스 카드의 버스도 그때까지의 AGP로 부터 PCI-Express x16 버스로의 이동기를 맞았다. 2004년 ~ 2005년, 유저는 그래픽 카드를 교체할 때 「 SM 2.0 (ATI)로 할지 SM 3.0(NVIDIA)로 할지」의 선택과 동시에 「AGP로 할지 PCI-Express로 할지」도 선택하지 않으면 안되었다.

어쨋든, NVIDIA는 다음해 2005년에는 2세대 SM3.0 대응 GPU 「 GeForce 7800 」 시리즈를 투입. 「2004년에는 SM 2.0의 숙성」을 지론으로 했던 ATI 도 2005년에는 NVIDIA에 뒤처진지 1년 반 만에 ATI 최초의 SM 3.0 대응 GPU인 「 Radeon X1800 」시리즈를 내놓았다. 하지만 Radeon X1800은 SM 3.0의 기본 특징에는 모두 맞추었지만 VTF는 지원하지 않았다.

이어서 2006년에는 NVIDIA는 3세대의 SM 3.0 대응 GPU「GeForce 7900 」시리즈를 발표했고, 이것에 대항해 ATI는 Radeon X1900 시리즈를 투입했다. 양사 모두 앞세대의 모델번호에 "+100" 을 한 단지 퍼포먼스 향상판 제품을 내놓아 성능면 이외에 눈길을 끌만한 부분은 없었다. 여전히 ATI는 Radeon X1900 시리즈에서도 VTF는 지원하지 않았다.

SM 3.0 대응 GUP가 ATI, NVIDIA 쌍두마차로 부터 모두 나온 것은 좋지만, VTF 기능의 지원에 대해서는 양사의 보조가 맞지 않아, SM 3.0에 있어서 VTF 기능은 마이너한 기능이 되고 말았다. 이런 근간 기능의 지원/미지원이 유저나 리얼타임 3D그래픽스 기술의 진화에 주는 영향은 적지 않고 반드시 올 다이렉트X 10을 맞아 큰 과제가 된 것 같다. (계속)

SM 3.0  특수기능으로 어필된 「 VTF 」. 하지만 ATI가 VTF를 지원하지 않았기 때문에 VTF의 효과는 이 NVIDIA가 많든 샘플데모 정도에서 밖에 볼 수 없었다.



 

가정용 게임기의 세계에서는 2005년말에 마이크로소프트의 2세대 게임기인 「Xbox 360 」이 발매 되었다. 그래픽스 기술적으로는 다이렉트X 9 세대 / SM 3.0 대응이다. 그리고 자세한 것은 뒤에 기술하겠지만, Xbox 360 - GPU는 ATI 제품으로, 의외로, 동일한 시기에 ATI가 PC용으로 발표한 Radeon X1800 시리즈와는 설계가 완전히 다르다는 점이 매우 흥미롭다. 덧붙여서 먼저 나온 VTF 기능은 Radeon X1x00 시리즈 전체가 지원하지 않지만, Xbox 360 - GPU는 지원하고 있다.

     마이크로소프트「Xbox 360」

 

Xbox 360용 게임소프트웨어「Gears of War」(2006년、Microsoft)로부터. 리얼타임 3D 게임그래픽스는 여기까지 왔다.
Gears of War and the Crimson Omen are either registered trademarks or trademarks of Epic Games, Inc. in the United States and/or other countries. All rights reserved. (C)2007 Microsoft Corporation. All Rights reserved. Microsoft, the Microsoft Game Studios logo, Xbox, Xbox360, the Xbox logos, Xbox Live and the Xbox Live Logo are either registered trademarks or trademarks of Microsoft Corporation in the United States and /or other countries.


2006년말에는, 소니로 부터도 새로운 게임기인 「 플레이스테이션3 」(PS3)이 발매된다. PS3의 GPU는 NVIDIA가 설계를 담당했고(제조는 소니 외), 「 RSX 」라는 전용명칭이 주어졌지만, 기본설계는 지포스 7800 시리즈와 거의 같고, 그래픽스 기술적으로는 경쟁상대인 Xbox 360과 동일 세대인 다이렉트X 9 세대 / SM 3.0 대응이였다.

            소니「 플레이스테이션 3 」


「MotorStorm」(SCE Europe)。그래픽 뿐만 아니라, 물리 시뮬레이션의 리얼리티도 기대되었던 PS3 게임.

MotorStorm TM (C)2006 Sony Computer Entertainment Europe. Published by Sony Computer Entertainment Inc. Developed by Evolution Studios. MotorStorm is a trademark of Sony Computer Entertainment Europe. All rights reserved.

신세대 게임기 전쟁에서는 「 Xbox 360 대 PS3 」이라는 구도이겠지만, 실은 아무일 아닌듯이, 여기에서도 「 ATI 대 NVIDIA 」의 싸움이 전개되고 있는 것이다.  어쨋든 2대 최신게임기의 그래픽스는 모두 프로그래머블 셰이더 3.0 아키텍처이고, PC도, 게임기도 SM 3.0 시대에 돌입했다.

여기에 게임 플랫폼의 또 하나의 영웅, 닌텐도는 신세대 게임기로 「 wii 」를 내놓지만 그래픽스 기술적으로는 진화를 거의 단념한 방침을 채택했다.


2007년~ Programmable Shader 4.0과 다이렉트X10 시대의 시작

1년간격으로 다이렉트X의 버전 번호가 바뀌었던 1990년대 후반과 다르게, 다이렉트X 9는 2002년 부터 4년간, 윈도우즈 XP의 라이프타임 전체를 거의다 차지했다. 2006년 후반에는 ATI가 CPU 메이커인 AMD에 합병된 사건이 일어나지만, 뒤돌아 보면, 2000년 이래로 리얼타임 3D 그래픽스의 역사는 실질적으로는 「 ATI 대 NVIDIA 」의 싸움의 역사였다고 말해도 좋을 것이다.

2007년, 새해가 열리자 마이크로소프트는 서둘러 새로운 OS인 「 윈도우즈 비스타 」를 발표했다. 이와 동시에 새로운 다이렉트X인 「 다이렉트X 10 」이 햇수로는 5년 만에 릴리즈 된다.

다이렉트X 10에서는, 프로그래머블 셰이더의 버전은 4.0에 대응하게 되었다.

SM 4.0에서는, SM 3.0에 비해 명령어 셋의 확충이 한층 더 일어났다. 구체적으로는 정수 연산 명령, 이진 논리 연산 명령 등이 추가되었고, CPU 같은 범용 프로그래밍이 가능한 기능들이 보다더 강화 되었다. 또, 명령어 셋적으로는 정점셰이더와 픽셀셰이더의 격차가 없고, 이것을 마이크로소프트는 「 Common Shader 」(common:공유형/범용형) 아키텍처로도 부르고 있다.

                   SM 3.0 와 SM 4.0 의 비교

동시에 텍스처 액세스 갯수도 SM3.0 때의 16개에서 SM4.0 에서는 128개로 확장되었고, 공통지수항 E를 가진 새로운 텍스처 포맷 RGBE 형식도 지원했고, 리얼타임(게임)을 겨냥한 저부하의 HDR 렌더링을 가능케 했다.

그리고 다이렉트X 10 / SM 4.0에서 최대의 토픽이라고 할 수 있는 세번째 프로그래머블 셰이더인 「 지오메트리셰이더 」(Geometry Shader)가 추가 되었다.

지금까지 정점차원의 것을 「 지오메트리 」라고 부르는 경우가 많았기 때문에 「 지오메트리셰이더 」는 「 정점셰이더 」와 똑같이 들릴지도 모르겠지만, 정점셰이더와는 다른것이다. 다만, 「 다루는 정보는 정점 차원 」이라는 점에서는 같다.

지오메트리셰이더의 역할은 프로그래머블하게 정점을 증가/감소 시키는 것. 정확하게 선분, 폴리곤, 파티클 같은 프리미티브(Primitive)의 증가/감소까지 가능하다.

그리고 정점처리 Phase에 지오메트리셰이더가 추가된 것에 호응해서 정점셰이더나 지오메트리셰이더의 출력을 비디오메모리 쪽에 쓰고(write) 다시 돌려주는 메모리 출력기능인 「 스트림아웃 」(Stream Output) 기능도 추가되었다. 이로인해, 정점처리 Phase에서 「 정점셰이더 -> 지오메트리셰이더 -> 정점셰이더..... 」라고 할수 있는 재귀적인 처리가 가능해졌다.

지오메트리셰이더와 스트림 출력의 구조는 고도의 3D 모델의 변형, 가공을 가능하게 하는데 머물지 않고, GPU를 CPU와 같은 범용처리 목적으로 활용하는 GPGPU(General Purpose GPU) 용도로도 유용하게 되어서 폭넓은 응용이 기대되고 있다.

      

다이렉트X 9 / SM 3.0에서의 렌더링 파이프라인

      다이렉트X 10 / SM 4.0 에서의 렌더링 파이프
      라인





 

다이렉트X 10 / SM 4.0은 윈도우즈 비스타 전용.
엄격한 버전컨트롤로 아류버전 없음.


 

다이렉트X 10 / SM 4.0은 윈도우즈 비스타에만 독점적으로 공급된다는 방침도 발표가 되어서, 윈도우즈 XP 이전 버전에는 제공되지 않게 되었다. 마이크로소프트는 윈도우즈 비스타 발매 직전에 「 윈도우즈 XP 지원 연장 」을 발표했지만, 「 다이렉트X 10 / SM 4.0 이 비스타 이후 버전 전용 」이라는 방침은 바뀌지 않았다. 실질적으로 실시간 3D 그래픽스의 진화는 윈도우즈 비스타 이후에 맡기게 되었다.  

실은, 이 다이렉트X 10 / SM 4.0의 비스타 전용공급은, 드라이버 모델의 큰 변화가 이유 중 하나로 되어 있다.

다이렉트X에 의해서 제공되어 온 그래픽 서브시스템인 Direct3D는, 복수의 어플리케이션으로부터 동시 사용되는 것을 설정하지 않았었다. 비스타에서는 이 GUI가 Direct3D 9(Ex)에 의해서 구현되어 있고, 이것과 별도로 Direct3D 10도 포함되어 있다. 동시에 복수의 3D 어플리케이션을 동작 시키거나, GPGPU 용도로의 대응까지도  고려한다면 이전의 싱글테스크를 전제로한 설계로는 맞지 않았던 것이다. 거기서 비스타라는 큰 변혁에 맞추어 릴리즈된 다이렉트X 10 / SM 4.0에서는, 멀티쓰레드에 대응해서 좀더 동작의 안정도를 향상시킬수 있는 새로운 GPU 드라이버 소프트웨어 아키텍처를 채용했다. 그것이 「 WDDM: Windows (Vista) Display Driver Model  」이다.

WDDM에서는 드라이버 소프트웨어가 유저모드와 커널모드로 나뉘어지고, 어플리케이션에 의한 부당한 드라이버 제어등으로 인한 시스템 크래쉬가 일어나기 힘들도록 설계되어 있다. 또, GPU의 하드웨어적인 멀티쓰레드 대응도에 맞추어 WDDM 1.0 / 2.1 이라는 버전으로 나누어져 있고, WDDM 1.0은 다이렉트X 9 세대 이전의 구설계의 GPU에 WDDM 적용한 것이고, 그리고  미리 다이렉트X 10을 타겟으로 개발된 GPU는 WDDM 2.0과 이후버전이 제공된다. 1.0과 2.0/2.1의 차이는 실질적으로는 멀티쓰레드 대응 레벨의 차이를 나타내며, 1.0는 Non-preemptive(*1)한 멀티쓰레드에 대응하고, 2.0/2.1은 Preemptive한 멀티쓰레드(*2)에 대응한다. 2.0과 2.1의 차이는 주로 멀티쓰레드 조밀성의 차이로, 2.1 쪽이 보다 작은 타겟 단위로 쓰레드를 교체 할 수 있다.

윈도우즈 비스타의 그래픽 서브시스템


(*1, *2) Non-preemptive(비선점)라는 것은 자발적으로 쓰레드 교체를 수행하는 멀티쓰레드 적용 방법. Preemptive(선점)는 타임쉐어링 등을 사용해, 자동적으로 스레드를 교체하는 멀티쓰레드 적용 방법.

다이렉트X 8에서 시작된 프로그래머블 셰이더이지만, SM 1.x / 2.0 / 3.0 이라는 버전 책정은 있지만 사실 GPU 메이커 마다 독자적인 확장이 있었기에 아무래도 최종적으로는 혼돈스런 부분이 있었다. 이런 아류버전의 혼재는 유저의 제품 선택을 어렵게 만들었을 뿐만 아니라, 소프트웨어 업계로 부터의 반발도 컷다. 거기에서 마이크로소프트에서는 다이렉트X 10 이후에서는 다이렉트X 9 이전에 존재했던 Caps(Capability Bits Test)라 불리는 그래픽 서브시스템의 지원을 테스트하는 기능을 없애고, 엄격하게 버전 컨트롤을 행한다는 방책을 명확히 내세우고 있다. 이것에 의해서 다이렉트X 10 세대 / SM4.0 대응을 내세우는 GPU는 다이렉트X 10 / SM 4.0의 모든 기능을 구현하지 않으면 안되게 되었다. 다이렉트X 9 / SM 3.0 시대의 NVIDIA:VTF 지원, ATI:VTF 비지원과 같은 일은 다이렉트X 10 / SM 4.0 시대에서는 일어나지 않는다는 것이다.

이것은, 리얼타임 3D 그래픽스의 기본기능을 갖추는 부분에 대해 일단락을 지은 것으로, 앞으로의 기능 강화가 매우 복잡하고 고도해져서 업계단체 내에서의 엄중한 논의를 거칠 필요가 생겨난 것과도 관계가 깊다.




 

통합형 셰이더 아키텍처와 미래의 다이렉트X


 

다이렉트X 10 세대 / SM 4.0 대응 1번 GPU는, 엔비디아의 GeForce 8000 시리즈였다. 약간 늦게, 경쟁사 ATI(AMD)는 RADEON HD2000 시리즈를 내놓았다.

엔비디아 「GeForce 8800 GTX 」. 최초의 DirectX 10 세대 / SM 4.0 대응 GPU는 앤비디아로 부터 릴리즈됬다.


ATI와 NVida, 양사의 다이렉트X 10 세대 / SM 4.0 대응 GPU는, 그 하드웨어에 「 통합형 셰이더 」아키텍처(Unified Shader Architecture)를 채용한 점이 특징이다.

프로그래머블 셰이더에는 정점셰이더, 픽셀셰이더, 그리고 새로운 지오메트리셰이더라는 3개의 셰이더가 있지만, 그리는 씬에 따라서 정점셰이더와 픽쉘셰이더의 부하는 변하게 되고, 새로운 지오메트리셰이더는 지금까지의 어플리케이션을 이용하는 한 그다지 활용할 만한 곳이 없다. 그럼에도 불구하고, 정점셰이더 몇기, 지오메트리셰이더 몇기, 픽셀셰이더 몇기라고 고정적으로 셰이더 유닛을 나누어서 GPU를 설계해 버리는 것은 낭비가  심하다. 각 셰이더는 확실히 각각의 특유의 역할을 하고 있지만, 실제의 연산내용은 벡터나 행렬 연산이 주이고 각 셰이더간에 그렇게 큰 차이가 없다.

그렇다면, "범용"으로서의 프로그래머블 셰이더 유닛을 많이 준비해서, 필요에 맞게 그것들을 정점셰이더로 쓰거나, 지오메트리 셰이더로 쓰거나, 픽셀셰이더로 쓰거나 하는 방법이 합리적이지 않을까?

이것이 통합형 셰이더 아키텍처의 기본적인 사고방식이다.

이것에 의해서, 정점 부하가 클때는 정점셰이더가 보다 많이 기용될 수 있고, 픽쉘 부하가 클 때는 픽셀셰이더가 많이 기용될 수 있게 된다.

 

종전의 GPU에서는 정점셰이더, 픽쉘셰이더의 어느쪽에서 병목현상이 생기면 성능을 내기 힘들 뿐만 아니라, 한가한 셰이더도 생겨난다.

   통합형 셰이더 아키텍처는 부하가 걸려
   있는 셰이더를 증가 시키는 대응으로
   병목현상을 감소 시킬 수 있다.

좀더 알기 쉽게. 왼쪽이 종전의 고정적으로 나누어진 방식의 아키텍처, 그리고 오른쪽이 통합형 셰이더 아키텍처의 이미지이다.

ATI는 PC 타겟의 다이렉트X 10 세대 / SM 4.0 대응 GPU의 출시에서는 NVIDIA에 비해 늦었지만, 「 통합형 셰이더 아키텍처 」의 구체화는 2005년 등장한 Xbox 360 - GPU에 대해 시행했고, 이 아키텍처에는 ATI 쪽에 좀더 경험이 있다고 말할 수 있다. 다이렉트X 10 세대 / SM 4.0 대응 GPU는 놓쳤지만 통합형셰이더 아키텍처 실용화의 첫번째 주자는 ATI였던 것이다.


 

2008년 이후~
미래의 다이렉트X 「 다이렉트X 10.1」과 「 다이렉트X 11 "이후"」


 

다이렉트X 10이 막 릴리즈된 2007년 3월, 샌프란시스코에서 개최된 게임개발자의회 GDC 2007에서는, 마이크로소프트로부터 다이렉트X 10 이후의 로드맵에 대한 언급이 있었다.

맨 먼저 다이렉트10의 마이너 버전인 다이렉트X 10.1이 릴리즈 되고, 프로그래머블 셰이더 버전은 4.1이 된다.

확장된 기능으로는, WDDM2.1의 본격적인 지원 이외에 멀티샘플방식의 안티에일리어싱(MSAA:Multi-Sampled Anti-Aliasing)의 샘플링 위치의 프로그래머블화, 복수의 렌더타겟 간의 개별 블렌딩 메서드 지원, 큐브맵 텍스처 배열 지원이라는 SM4.0의 기능 확장이 추가된다. 물론, 기능 확장의 보조를 맞추기 위해서 10.1이라는 "0.1" 단수는 포함되지만, 마이크소프트가 빈틈없이 버전 컨트롤을 하기 때문에 작은 아류의 등장은 없다.

다이렉트X 10.1 / SM4.1은 2008년 3월 예정인 윈도우즈 비스타 서비스 팩1에 포함될 예정이고, SP1 적용 후에 이용이 가능하다. 현시점에서 다이렉트X 10.1 / SM4.1 대응의 GPU로는 AMD(ATI)가 Radeon HD 3000 시리즈를, S3 Graphics가 Chrome 400 시리즈를  발표했는데, 한편 NVIDIA는 DirectX 10.1 / SM 4.1 대응 GPU는 당장 내놓지 않겠다는 방침을 분명히 한 상태이므로 다이렉트X 10.1 / SM 4.1이 메이져가 될지는 알 수 없다. 결국, 사실상 다이렉트X 10 세대에서도 AMD(ATI)와 NVIDIA의 방침은 엇갈렸다.

AMD의ATI Radeon HD 3870.
다이렉트X 10.1 / SM4.1 대응 1호 GPU.

S3 Graphics의 Chrome 440GTX.
메인스트림 보다 싼가격의 다이렉트X 10.1 / SM 4.1 대응 GPU로 구매욕구를 사로 잡는다.


그리고, 한층 더, 마이크로소프트는 다이렉트X 11의 릴리즈를 예정하고 있다.

다이렉트X 11 "이후"라는표현을 사용해서, 어떤 기능이 언제 추가될지는 미정이지만 어느정도의 큰 변화가 있을지에 대해서는 분명히 하고 있다.

한가지는 GPU를 보다 범용프로세서적으로 활용할 수있는 기능의 추가이다. 특히 처음에는 미디어프로세서적인 스트리밍프로세서로써 사용하기 쉽게 해줄 계획인 것 같다. 구체적으로는 GPU에 전송되어 오는 벡터 데이터를 보다 유연하게 확장한다든지, GPU에서 처리한 출력 데이터 스트림을 처리결과 단위로 스위칭해서 복수의 하드웨어에 분배하도록 하는 기능을 검토하고 있다고 한다.

또, 3D그래픽스 이외의 과학기술계산 용도에 대응하기 위해서 연산정도를 현재보다 배정도인 64비트 부동소수점(FP64)에도 대응한다고 한다. 기능적으로 이런 대응을 하는 것은 물론이고, 그런 범용 프로세서로써의 성능강화를 위해 아마도 GPU쪽에는 메모리 런타임 Access 기능 강화나 캐쉬시스템 개량도 해줄 것이다.

나아가, 다이렉트X 10 / SM4.0의 지오메트리셰이더에 덧붙여, 다이렉트X 11 이후에서는 렌더링파이프라인에 새로운 처리 스테이지가 추가된다. 그것이 「 테셀레이션」 (Tessellation) 셰이더이다. 테셀레이션은 간단히 말하면, 폴리곤을 어떤 메서드에 따라 분활하는 기능이다. 이미 다이렉트X 9 / SM2.0 시대부터 테셀레이션 지원은 있었지만, 특정 GPU를 활용하는 경우로 한정되어 있었기 때문에 주류적 기능은 아니였다. 하지만 다이렉트X 11 이후에서는 이것을 정식 표준(standard)기능으로 규정한다고 한다. 역시, 테셀레이션을 수행하는 유닛, 즉 「 테셀레이터 」(Tessellator)는 처음에는 프로그래머블 셰이더로서가 아니고, 고정기능유닛으로써 제공된다는 방침도 보여지고 있다. 덧붙여서, 현재 다이렉트X 규격은 아니지만 Radeon HD 3000 시리즈는 고정기능으로 테셀레이션 유닛을 탑재하고 있다. 이것이 다이렉트X 11에서 정식사양이 될지는 명확하지 않다.

테셀레이터는 고정유닛이 되지만, 그 대신 폴리곤 분활을 수행할 때의 제어점을 프로그래머블하게 컨트롤 하는 「 컨트롤 포인트 셰이더 」(Control Point Shader)라고 하는 제 4의 프로그래머블 셰이더가 추가된다. 이것은 실질적으로는 테셀레이터의 작동을 보조하는 프로그래머블 셰이더라고 해야 할 것이고, 다이렉트X 11 이후, 나아가 미래의 다이렉트X 세대에서는 통합될 가능성이 있다.

다이렉트X 11 이후의 렌더링 파이프라인 예상도

이외에, 다이렉트X 11 이후에서는 리얼타임 3D 그래픽스의 다년간의 과제였던 반투명 오브젝트의 그리는 순서에 의한 속박으로부터 해방을 목표로하는 「 A-Buffer 」의 개념을 어떤 형태로라도 도입해 나가고 싶다고 발표가 되었다.

리얼타임 3D 그래픽스에서는 반투명 오브젝트는 뒤에서 앞으로 그리지 않으면 반투명 묘사가 올바르게 재현되지 않기 때문에 그리기 전 준비로 3D 오브젝트를 정렬하는 Phase가 필요했다. 「 A-Buffer 」에서는, 렌더링 시에 셰이더에서 산출된 칼러 외에 윤곽 마스크 정보나 깊이값 정보등을 함께 프레임버퍼에 써 넣는 것으로 최종 렌더링 후에는 앞뒤가 맞도록 영상을 재구성할 필요가 있지만 렌더링 시에는 그리는 순서의 의존성을 완전히 배제할 수 있는 메리트가 있다.

원래 A-Buffer는 죠지 루카스가 이끄는 Lucas Film이 오프라인 렌더링용으로 고안했던 기술이고, 렌더링이 시작된 시점에서는 GPU 쪽으로부터 그런 복잡성을 예견 할 수 없는 구조이다. 그래서, 그 리얼타임/하드웨어 기능은 상당히 복잡한 구조가 될 것이라고 예상된다. 하지만, 이 A-Buffer의 구조는 3D 어플리케이션 개발자나 3D 엔진 설계자에게 있어서는 소프트웨어쪽 부담을 대폭 격감 시킬수 있어서 그 기능추가에는 상당히 기대가 크다.

다이렉트X 11의 등장 시기는 전혀 모른다. 역시 제 4의 프로그래머블 셰이더가 추가되는 것으로 부터 생각해 보면 프로그래머블 셰이더 버전이 "1.0" 올라서, SM5.0이 되고나서 봐야 될 것 같다.

A-Buffer의 개념도.
"A"에는 anti-aliased、area-averaged(영역평균화)、accumulation(연산)이라는 복수의 의미가 들어 있다.




 

Posted by 노을삼킨별
,
아래에서 퍼옴
http://blog.naver.com/kingsalsero?Redirect=Log&logNo=70015615005


언리얼 엔진 3 ( Unreal Engine 3 )

가격
· 1 타이틀당 $500,000 (업그레이드 1년간 지원)
o 1년 후 업그레이드 1년에 $100,000
§ 원래는 크게 두가지의 버전으로 나뉘어서 언리얼 엔진 3.0 최종버전 (2008년)까지 무상 업그레이드, 언리얼 엔진 3.5 (2010년)는 추가로 비용을 지불 하고 업그레이드 받게 할 정책이었으나 업데이트의 빈도가 잦고 업데이트 내용이 많고 새로운 기술, 툴, 서드파티 기술의 IPP의 추가가 많아짐에 따라서 크게 3.0, 3.5로 두가지 버전으로 나누지 않고 언리얼 엔진 4가 나오기 이전인 2010년까지 지속적으로 꾸준한 업데이트가 되며 1년당 유지보수 비용을 받는 정책으로 바뀌었다. 업데이트는 꾸준하게 진행된다. 1년간 업데이트가 $100,000씩 하는 이유는 꾸준한 버전업에 엔진과 툴셋의 많은 기능개선과 발전이 있기 때문이다.
o 1 플랫폼 추가당 $50,000
o 멀티플 라이센스시 타이틀당 가격이 내려간다.
· 계약 조건에 따라서 가격이 상이하게 다르다.
엔진 역사 : (이전 역사는 언리얼 엔진 2 참고)
언리얼 엔진 2.0은 매우 많은 게임들에 사용되었으며 PC용 FPS에서는 거의 언리얼 엔진이 관례가 되었다. 이 후에 64비트의 지원과 여러가지 향상점이 있는 언리얼 엔진 2.5가 나오고 그 다음 버전으로 현재의 언리얼 엔진 3.0이 나온다.
언리얼 엔진 3.0은 기존의 장점인 깔끔한 코드 구조가 더욱 커스터마이징하기에 좋고 확장성이 더욱 증대되어 이전 버전보다도 더욱 융통성이 좋게 개선되었으며 개발 툴 역시 전보다 많은 진보를 이루어서 차세대 게임 개발에 편리한 많은 잇점을 제공한다. 뿐만 아니라 이제는 더 이상 FPS 장르에만 특화된 게임 엔진이 아니며 모든 장르를 쉽게 수용할 수 있는 구조로 크게 개선이 되었으며 엔진 코드와 게임 코드의 뚜렷한 경계를 지었음에도 엔진의 유연성과 코드의 연동성은 기존보다 더 개선되었기 때문에 프로그래밍시의 많은 기술적 난점을 해소하였다. 그리고 이젠 PC 뿐만 아니라 콘솔 기기도 본격적으로 지원을 함으로서 차세대 콘솔을 완벽지원하며 차세대 콘솔 기기인 Xbox 360와 PlayStation 3의 공식 미들웨어로 언리얼 엔진 3가 선정되었다.
지금 해외에선 언리얼 엔진 3가 차세대 콘솔 및 PC 게임 개발의 관례가 되어있으며 앞으로 게임 엔진 미들웨어의 중요성과 필요성이 점점 부각되어가고 있는 시점에서 지난 콘솔 세대의 미들웨어 선두주자인 렌더웨어(RenderWare)를 완전히 압도적으로 제치고 게임 엔진 미들웨어 시장의 선두주자로 달리고 있다.
렌더웨어가 단순히 그래픽스 라이브러리에 그쳐있는 반면에 언리얼 엔진은 버전업을 거듭하면서 방대한 게임 엔진으로 거듭났고 단순히 방대한게 아니라 방대한 엔진이 깔끔한 구조와 편리한 개발툴을 제공함으로서 개발 프로세서에 큰 향상을 주기 때문에 개발자들에게 크게 각광받고 있다.
언리얼 엔진 3가 처음 라이센스되기 시작했을 때는 미완성된 엔진이라는 말들도 조금 떠돌기는 했었으나 2007년 3월 현재는 매우 안정성이 좋으며 이전보다 더 향상된 툴과 나아진 엔진의 퍼포먼스와 다양한 미들웨어 IPP를 제공함으로서 훨씬 향상된 모습을 갖추고 있다.
언리얼 엔진 3는 다음 세대의 언리얼 엔진인 언리얼 엔진 4 (2010년 말경이나 2011년 초경에 출시할 예정이다)가 나오기 이전인 2010년까지 꾸준하게 현저한 향상과 두드러지게 새로운 특징들의 추가(significant enhancement and adding major new features)가 있을 것이라고 강조하고 있다.
· 작년 11월에 발매된 기어스 오브 워에 쓰인 언리얼 엔진 3 버전과 현재 2007년 3월자 언리얼 엔진 3 최신 버전을 비교해보면 불과 몇달정도 사이에 큰 렌더링 엔진이나 여러가지 부분에서 큰 향샹이 이루어졌다.
개발사 : 에픽 게임즈 (Epic Games)
엔진 타입 : 통합형 범용 게임엔진
· 언리얼 엔진 3는 더 이상 FPS에 특화된 게임엔진이 아니다. 언리얼 엔진 2를 포함한 쥬피터 엔진 시리즈, 시리어스 엔진 시리즈, 크라이 엔진 시리즈등의 다른 통합형 FPS 게임엔진들은 기본적으로 FPS에 맞추어진 엔진이라서 MMORPG나 다른 장르의 개발시에 변경에 어느정도의 어려움이 있다. 하지만 언리얼 엔진 3는 그런 어려움이 전혀 없이 매우 순조롭게 커스터마이징이 가능하다. 다양한 장르를 고려하기 때문에 심리스 월드도 기본 지원된다. 그리고 FPS용 네트워크 엔진이나 MMO용 네트워크 엔진이 따로 제공되며 액션 게임용 인공지능 엔진이나 MMO용 길찾기 엔진등 다양한 게임의 환경과 기획과 문제점들을 고려한 기술들이 제공된다.
엔진 구성 및 업데이트
· 이전까지와는 다르게 게임 코드와 엔진 코드가 완전히 분리되어 제공된다. 즉, 순수한 언리얼 엔진으로서 코드가 제공되는 것이다. 그리고 게임 코드와 엔진 코드 및 확장 모듈등의 각 코드간의 경계를 이전보다 더욱 명확하게 함으로서 엔진의 구조적 융통성과 커스터마이징이 용이한 구조와 뛰어난 확장성은 전보다 더욱 강화되었다.
o 그리고 보너스로 에픽에서 개발하는 게임의 모든 소스 코드도 제공된다.
· 엔진의 build 넘버가 올라갈수록 더 엔진에 기능이 추가되거나 새 툴셋이 추가되거나 기존의 성능이 더 향상된다. 기어스 오브 워가 나왔을 때의 엔진보다 언리얼 토너먼트 3에 사용되고 있는 엔진의 버전이 더 높으며 기능이 더 많고 더 최신 기술들이 사용되었다. PC 버전에서는 새로이 출시되는 하드웨어를 활용한 새로운 기술들이 지속적으로 업데이트 되며 콘솔 버전에서도 기술의 추가가 똑같이 이루어지지만 PC에서만 사용 가능한 기술들이 더 많다. 그리고 엔진 버전업이 되면서 최적화 작업도 계속 이루어진다.
제공되는 소스
· 순수한 Unreal Engine 3 (UnrealEd 및 모든 유틸리티 포함)의 Full source code
o 매주 업데이트되는 Unreal Engine 3의 새로운 버전 build codedrop
· Epic의 Unreal Engine 3 사용 게임들의 Full source code
o Gears of War의 Full source code
o Unreal Tournament 3의 Full source code
· UDN에서 Demiurge Studios에 의해 지속적으로 추가 제공되는 Customize code와 Example code
제공되는 툴
· UnrealEd (언리얼 에디터)
o 기본적인 모습으로는 언리얼 엔진 2의 것과 거의 유사한 모습이지만 다수의 변경과 기존 기본적인 인터페이스 및 기능의 향상이 이루어졌으며 비주얼 셰이더 시스템이나, 파티클, 컷신툴 인공지능 툴셋, 사운드 설정 툴셋, 비주얼로 게임 스크립트짜는 툴같은 기존의 툴들의 성능 및 옵션 개선 및 새로운 툴 추가, 모든 툴은 UnrealEd 내부에 통합되어 있으며 각 툴간에 긴밀하게 상호 연동된다. 
o 주요 세부 툴
§ UnrealMatinee :UnrealEd 내에서 편리하게 도와주는 컷신 제작 툴셋 (언리얼 엔진 2버전에 비해 비약적으로 향상) 
§ UnrealCascade : 언리얼 엔진 2의 파티클 위저드 및 언리얼 엔진 2.5의 파티클 시스템 에디터에 비해서 비약적으로 향상된 파티클 툴셋 
§ UnrealKismet : 게임 플레이나 인공지능 설정등의 모든 스크립팅을 플로우챠트 형식으로 만들어서 비주얼적으로 개발하는 툴셋, UnrealScript를 자동 생성하며 UnrealMatinee로 컷신 스크립트를 짤때도 사용 가능하며, UnrealEd 내의 모든 툴의 스크립트 생성에 사용 가능하다.  
§ Visual Material Editor : 비주얼 툴셋으로 재질 셰이더를 설정하면 자동으로 Cg와 HLSL코드가 생성된다.  
§ Visual Terrain Editor : 실시간 비주얼로 보이는 그대로 지형을 생성하고 초목을 장식하고 광활한 실외 지역을 작업하는 툴 
§ SoundCue Editor : 사운드의 설정을 해주는 툴로 작업환경 전체에 걸쳐서 다양한 세부적 사운드 설정을 해주는 툴 
§ UnrealPhAT : 물리의 모든 세부적인 설정을 해주는 툴로서 게임내 모든 환경과 오브젝트, 탈것, 캐릭터에 물리를 실시간으로 세부적인 설정이 가능한 툴  
§ AnimSet Editor : 모든 애니메이션 세트와 애니메이션 시퀀스를 설정하는 툴 
§ AnimTree Editor : 애니메이션을 비주얼 트리 모양으로 나열해서 한눈에 알아보기 쉽게 구성해 놓고 설정을 할 수 있는 툴 
§ Script Editor : UnrealEd 내에서 모든 스크립트를 바로 열어서 확인 가능하고 수정한 후에 즉석에서 인터프리팅해서 실행할 수 있으며 컴파일해서 적용 시킬수도 있는 툴 
§ Play In Editor : 툴 내에서 직접 모든 것이 적용된 게임 자체를 돌려서 확인하고 작업할 수 있는 툴 
· COLLADA import path for meshes and animation
· 적당한 노말값을 추정하여 노말맵을 생성하는 툴  2,000,000 폴리곤 (노말맵을 추출할 모델)  5,287 폴리곤 (실제 게임에 사용될 모델)  5,287 폴리곤이 사용된 모델에 노말맵을 적용(엔진상에서 보이는 모델)  1,000,000의 폴리곤으로 노말맵을 추출해서 적용한 배경씬  실제 게임상의 폴리곤 수는 500,000
· 디버깅 툴
· 퍼포먼스 모니터링 툴
· UnrealActorX Exporter
o 3D Studio MAX 플러그-인
o MAYA 플러그-인
o Softimage XSI 플러그-인
o 3D Studio MAX, MAYA, Softimage XSI에서 만든 메쉬를 UnrealEd로 곧 바로 적용할 수 있는 플러그-인이다.
· Unreal Engine 3 IPP의 모든 툴셋이 UnrealEd에 연동
o PhysX 물리 엔진 툴셋
§ PhysX 물리 엔진에서 제공하는 SDK 툴셋과 유틸리티들
o FaceFX 툴셋
§ FaceFX에서 제공하는 페이셜 애니메이션 및 립싱크 제작 툴셋
o 이 외에도 Unreal Engine 3 IPP의 툴들을 포함한 다른 툴들이 더 존재하며 언리얼 엔진 3는 2010년까지 업데이트 될 예정으로서 앞으로도 더 많은 툴셋과 유틸리티들이 추가 될 예정에 있다.
제공되는 컨텐츠
· Unreal Engine 3 Example
· Epic의 Unreal Engine 3 사용 게임들의 Example
o Gears of War의 Example contents
o Unreal Tournamentr 3의 Example contents
o Unreal Engine 3 Example contents는 상용 프로젝트에 도입해도 상관없으나 Gears of War나 Unreal Tournament 3의 Example contents는 상용 프로젝트에 도입하면 안된다. 참고용으로만 사용해야 한다.
· Demuirge Studio의 다양한 예제
o Demiurge Studio는 Unreal Developer Network의 지원팀으로 언리얼 엔진의 QA와 개선사항을 담당하고 엔진의 활용법과 다양한 예제를 개발하고 지원팀으로서 Q&A를 진행해준다.
엔진 개발사에서 만든 게임
· 기어스 오브 워
o Xbox 360 - 2006년 11월 7일 출시
o PC - 2008년 출시 예정
· 언리얼 토너먼트 3
o PC - 2007년 출시 예정
o Xbox 360 - 2007년 출시 예정
o PlayStation 3 - 2007년 출시 예정
· 기어스 오브 워 2
o Xbox 360- 개발 예정
o PC - 개발 예정
· 언리얼 3
o PC - 개발 예정
o Xbox 360 - 개발 예정
o PlayStation 3 - 개발 예정
이 엔진을 사용한 대표적인 게임
· 기어스 오브 워
· 레인보우 식스 베가스
· 언리얼 토너먼트 3
· 헉슬리
· 리니지 3
국내에서 쓰이는 게임
· 웹젠의 헉슬리
· 레드덕의 아바와 프로젝트 M(신작 MMORPG라고 한다)
· NC소프트의 리니지 3와 프로젝트 M(김형태씨가 참여해 만드는 MMORPG라고 한다) 및 온라인 FPS 게임을 비롯한 몇개의 작품들
· 소프트맥스의 마그나 카르타 2(Xbox 360용)
· 애니파크의 A4(A3 차기작이며 A4는 임시제목이다)
· CJ 인터넷의 미발표한 MMOG
· N모사의 신작 MMORPG (NC말고 다른 N모사)
· 모사의 프로젝트 F (기사에는 모회사의 프로젝트 F가 언리얼 엔진 3를 사용한다고 나옴)
· 그 외에 아직 미발표한 다수의 개발사들이 언리얼 엔진 3를 사용해서 개발중인데 국내에서는 대부분 FPS나 MMORPG가 많다.
국내에서 엔진을 쓰다가 게임이 취소된 경우
· 웹젠의 PlayStation 3용 게임인 엔드리스 사가
o 언리얼 엔진 3가 게임과 맞지 않다거나 엔진 자체의 문제점으로 취소된 것은 아니고 PlayStation 3의 열악한 개발조건과 회사 수지타산이 맞지 않아서 취소된 경우이다.
§ 그러나 라이센스비용이 낭비된 것은 아니다. 웹젠은 이미 언리얼 엔진 3를 사용해서 헉슬리를 개발하고 있었으며 엔드리스 사가도 언리얼 엔진 3를 사용해서 개발했지만 이미 한번 라이센스 한 상태에서 비용은 더 들지 않으며 게임이 완성되어 출시했을 때 추가비용이 들어가는 것이기 때문이다.
· 엔드리스 사가 외에는 취소된 경우가 아직까지는 없다.
해외에서 쓰이는 게임
· 뛰어난 기술력과 편리한 차세대 툴셋과 뛰어난 확장성으로 PlayStation 2 및 Xbox 세대에서 표준으로 사용되던 렌더웨어(RenderWare)를 제치고 PlayStation 3 및 Xbox 360 세대부터는 콘솔 및 PC 게임 및 온라인 게임 개발 시장을 장악하다시피 하고 있다.
· 다양한 장르에 많이 쓰이며 FPS나 3인칭 액션, 어드벤쳐, RPG, RTS, 카툰 스타일의 캐주얼 게임, MMORPG등 장르를 구분하지 않고 많이 쓰인다.
· MMPOG들도 이미 상당수가 언리얼 엔진 3를 채용하고 있다.
o 시질 게임즈 온라인의 뱅가드 : 사가 오브 히어로즈 (Vanguard : Saga of Heroes)의 확장팩 (뱅가드 원본은 언리얼 엔진 2를 사용해서 만들어졌다)과 차기작
o 샤이안 마운틴 엔터테인먼트의 스타게이트 월즈 (Stargate Worlds)외에 신작 게임들을 언리얼 엔진 3를 사용하여 더 개발할 예정임
o 소니 온라인 엔터테인먼트의 DC 코믹스/유니버스 온라인(DC Comics/Universe Online)와 에버퀘스트 3를 포함해 총 5종의 온라인 게임을 언리얼 엔진 3를 사용해 개발 중
o 오란(Auran)의 퓨리 (Fury)외에 신작 게임들을 언리얼 엔진 3를 사용하여 더 개발 할 예정임
o 보그스터 엔터테인먼트 (Vogster Entertainment)의 크라임크래프트 (Crimecraft)외에 신작 온라인 게임 몇종을 더 언리얼 엔진 3를 사용해 개발 중
o 마이크로소프트 게임 스튜디오의 신작 온라인 게임들 몇종을 언리얼 엔진 3를 사용해 개발 중
o 리얼 타임 월즈 (Realtime Worlds)의 APB (All Point Bulletin)
o 중국의 개발사 천청의 신작 MMORPG
o 중국의 개발사 소프트-월드 (Soft-World)의 신작 MMORPG
o 이 외에 다수의 개발사들이 언리얼 엔진 3를 사용해 온라인 게임을 개발중이거나 개발할 예정에 있다.
· RTS 장르를 만들기 위해서도 이미 몇 개발사들이 언리얼 엔진 3를 채용하고 있다
o 에이지 오브 엠파이어 (Age of Empire) 시리즈를 만든 앙상블 스튜디오 (Ensemble Studios)에서 언리얼 엔진 3를 사용해서 헤일로 워즈 (Halo Wars)와 새 RTS 게임을 만들고 있다.
o 코헨 (Kohan) 시리즈의 개발사인 타임 게이트 스튜디오 (TimeGate Studios)는 언리얼 엔진 3를 다중 라이센스 계약해서 신작 RTS와 신작 FPS와 신작 RPG 타이틀을 사용할 계획에 있다
· 현재까지 약 50여곳 이상의 업체에서 현재 300여개 이상의 타이틀 개발중, 더 늘어날 것으로 추정된다.
o 소규모 개발사에서는 싱글 라이센스로 사용하거나 큰 유통사의 지원을 받아 사용하는 경우가 많다
o 대규모 개발사에서는 대부분 멀티플 라이센스 계약을 체결해서 사내의 여러 스튜디오 및 팀에서 사용하게 한다.
· 특히 엔진 구조가 매우 플랙시블한 장점을 한껏 활용해서 대형 회사같은 경우에는 멀티플 라이센스로 회사 전체에서 공유하면서 여러 게임에 커스터마이징 되고 그렇게 된 부분들의 장점들을 또 다시 추려내서 각 게임마다 다시 적용하는게 쉬워서 그런 장점으로도 많이 사용되고 있다. 깔끔하고 좋은 코드의 구조와 연동성을 갖지 못한 엔진은 그렇게 할 수 없다. 그런 경우엔 코드가 거의 엉망진창이 된다.
o 언리얼 엔진 2는 특히 유비소프트에서 그렇게 잘 사용하였다.
o 언리얼 엔진 3는 그렇게 사용하는 개발사가 상당히 많다. 해외 서양쪽이나 일본쪽의 대형 유통사 및 개발사들의 경우에 많은 회사들이 다수의 게임에 사용하기 위해서 언리얼 엔진 3를 멀티플 라이센스를 하였다. 언리얼 엔진 3부터는 그런 장점들이 타 플랫폼으로의 이식에도 용이하도록설계되어 있다.
업데이트 : 지속적으로 개량 중, 최적화, 새로운 렌더링 기술, 새로운 툴셋, 기타 요소 등등
· 언리얼 엔진 3는 현재 64-bit 프로세싱, PC와 차세대 콘솔 기기들의 멀티 쓰레딩, Windows Vista와 Direct3D 10의 완전한 활용 및 복셀 렌더링, 새로운 지형 시스템, 실시간 래디오시티같은 신기술이 구현되어 있으며 앞으로도 언리얼 엔진 4가 출시되기 이전인 약 2010년정도까지 계속해서 꾸준하게 엔진이 버전업 되면서 신기술의 추가와 더 편리한 툴셋과 IPP의 추가등 현격한 향상이 있을 것이다.
언리얼 엔진 3의 특징
· C++ DLL 모듈 기반
o Plug-in 컴포넌트화
· 기존의 언리얼 엔진 2보다도 더욱 커스터마이징이 용이한 구조로 개선되었다.
o UnrealScript의 사용으로 여러 모듈 결합이 매우 용이한 점은 전보다 더욱 확장하기 쉽게 개선되었다.
§ 언리얼 엔진이 다른 엔진들과 가장 차별화되는 독보적인 특징으로서 UnrealScript를 사용하여 다양한 모듈을 제어하고 연동할 수 있다는 점이다.
§ 언리얼 엔진에서는 모든 모듈이 언리얼 엔진에서 정의한 각각의 모델로 구성되어 있으며 기존의 존재하는 모델이나 모델 타입 또는 새로운 모델이나 새로운 모델 타입을 추가/변경/삭제가 가능하며 이 모든 것들은 커스터마이징이 가능하다.
§ 모든 모듈과 모델은 UnrealScript를 통해 언리얼 엔진 전체와 매우 유연하게 연동된다. 직접 엔진 코드와 네이티브 코드를 연동을 시키지 않고서도 UnrealScript만을 사용해서도 외부의 모듈을 언리얼 엔진 내에 연동 시켜서 사용할 수 있으며 이런 연동 작업은 UnrealEd 내에서 매우 간단하게 가능하다. UnrealScript를 쓰지 않고 네이티브 코드만 연동 시키는 경우에도 유연한 코드 구조 덕분에 다른 엔진보다 쉬운 장점을 제공하지만 UnrealScript를 쓰는 것은 전혀 어렵지 않으므로 꼭 쓰길 권한다.
o 엔진 제어나 연동 외에도 게임 플레이 스크립트도 UnrealScript로 작성할 수 있다.
§ 게임 플레이 프로그래머/스크립터의 취향이나 성향에 따라서는 C#, Java Script, Rube, Lua와 같은 외부 언어를 적용하거나 외부 언어와 UnrealScript 동시 사용이 가능하다.
§ UnrealScript와 외부 언어 2개 이상 또는 외부 언어만 2개 이상을 동시 사용하는 것도 유연한 코드 구조 덕분에 쉽게 가능하다.
§ 각각 모듈별로 언어를 적용하여 사용이 가능하다.
· UnrealEd와 그 내부의 세부 툴은 C#이나 Visual Basic, Java APP등의 언어로 개발하여 연동하기가 쉬우며 이식도 간단하게 가능하다.
· 엔진 코드와 게임 코드의 명확한 경계로 프로그래밍이 훨씬 수월하게 개선되었다. 언리얼 엔진 2까지는 엔진 코드와 게임 코드가 어느정도 상호 의존적이었다. 그러나 언리얼 엔진 3에서는 그 구분이 더욱 명확해져서 프로그래밍 작업상의 훨씬 효율적인 잇점을 제공한다.
o 엔진을 커스터마이징하여 게임을 개발하던 도중 엔진을 업그레이드 하거나 다른 변경 사항을 적용해도 엔진을 크게 다시 손 볼 필요가 없이 손쉽게 적용 가능하다.
o 각 부분의 코드들의 명확한 경계가 있지만 내부적으로 연동성은 매우 좋다.
· Unreal Engine 3 IPP (Integrated Partners Program)
o 언리얼 엔진 3에선 IPP를 추진하고 엔진에 지속적으로 업데이트 해나간다. 언리얼 엔진 3 IPP는 에픽 자체의 기술이 아닌 외부의 각 분야마다 최고의 미들웨어 솔루션을 라이센스 해서 언리얼 엔진 3에 통합하는 것으로서 그 것은 렌더링 관련, 사운드, 네트워크, 인공지능과 같은 각 분야별 관련 미들웨어나 SDK, 그리고 외부의 다양한 툴셋마저도 통합하는 것을 포함하며 IPP에 포함된 미들웨어들의 라이센스 비용을 따로 필요로 하지 않으며 모두 언리얼 엔진 3 사용 개발사들에게 무상 제공된다. 언리얼 엔진 3 IPP가 생기게 된 연유는 다음과 같다.
§ 현재는 기술의 발전으로 게임 개발에는 고도의 프로그래밍 기술이 필요하게 됐으며 그에 따라서 상용 엔진의 사용이 대세이다. 계속 발전하는 기술은 분야별로 세밀한 각각의 전문적인 엔진들 (렌더링, 애니메이션, 라이팅, 지형, 인도어, 물리, 사운드, 네트워크, 인공지능, 립싱크, 초목 구현 등)에 따라서 한 분야별 엔진들이 만들어지고 라이센스 되기 시작했다. 이 것은 엔진 개발 비용상으로도 어쩔 수 없는 문제이기도 하지만 발전하고 복잡해진 기술로 인해서 각각의 분야별로 전문적인 최고의 솔루션이 만들어지기 위해서는 어쩔 수 없는 현상이다.
o 언리얼 엔진 3는 이러한 각 분야마다 최고의 기술들을 제공하기 위해서이며 또한 만들고자 하는 게임에 따라서도 요구하는게 달라질 수 있으므로 같은 분야라도 (네트워크 부분의 모듈은 현재까지 자체 1개와 2개가 추가됐고 라이팅 관련은 자체와 IPP 1개, 인공지능은 자체 1개와 IPP 2개 등) 여러가지의 외부 기술들을 라이센스 해서 언리얼 엔진 3에 포함함으로서 진정한 최고의 범용 게임 엔진으로서의 길을 걷고 있다. 외부의 기술은 언리얼 엔진 3 라이센스시 그냥 번들 형태로 제공되는게 아니라 언리얼 엔진 3의 잘 짜여진 구조를 바탕으로 외부의 모든 모듈들이 서로간에 긴밀하게 연동되어 있으며 언리얼 엔진 3를 라이센스하면 이 모든 외부 IPP 기술들을 추가적인 비용없이 무상으로 제공받을 수 있다. 연동된 기술들은 언리얼 엔진 3 구조하에 연동된 만큼 언리얼 엔진 3의 기본적인 구성요소와 마찬가지로 UnrealScript를 통해 제어할 수 있으며 UnrealEd에서 곧 바로 사용 가능하며 언리얼 엔진 3의 기본적인 확장 기능성과 마찬가지의 특성을 가지고 있으므로 언리얼 엔진 3와 따로 떨어진 상태로 외부 미들웨어 모듈을 직접 사용하는 것보다 언리얼 엔진 3상에서 순조롭게 커스터마이징이 가능한 잇점도 제공한다. 그리고 언리얼 엔진 3 IPP에 포함된 미들웨어들은 한번의 업데이트로 끝나는 것이 아니라 해당 미들웨어들의 버전업을 수시로 체크하여 언리얼 엔진 3에 지속적으로 최신 버전으로 업데이트 된다.
플랫폼 지원
· 32-bit Windows (Windows XP/Vista, Windows Vista와 DirectX 10 완벽 대응)
· 64-bit Windows (Windows XP/Vista, Windows Vista와 DirectX 10 완벽 대응)
· 32-bit Linux
· 64-bit Linux
· Mac OS X
· Xbox 360 (Gears of War를 통해 Xbox 360상에서의 성능은 충분히 증명이 되었으나 더 향상을 위해 개선중에 있다)
· PlayStation 3 (아직 퍼포먼스상에 문제가 있다. 계속 개선중에 있다)
멀티 쓰레딩
· Xbox 360 (Xenon 프로세서의 트리플 코어에 최적화된 멀티 쓰레딩)
· PlayStation 3 (Cell 프로세서의 다중 코어에 최적화된 멀티 쓰레딩)
· PC (2-코어, 4-코어, 8-코어에 각각 최적화된 멀티 쓰레드 렌더링/멀티 쓰레딩/멀티 쓰레드 프로세싱)
o 렌더링 시스템을 멀티 쓰레드로 처리
o 렌더링, 물리 연산, 인공지능, 사운드를 각각 멀티 쓰레드로 처리
o 기타 모듈을 멀티 쓰레드로 지정하여 처리
로딩 시스템
· Zone-based Loading System
· Backgrounds Loading System
· Seamless Loading System
렌더링 엔진
· 멀티 쓰레딩 렌더링 지원
· 렌더링 API 지원
o Direct3D 9 (Windows XP/Vista, Xbox 360)
o Direct3D 10 (Windows Vista)
o OpenGL 2 (Windows XP/Vista, Linux, MacOS X, PlayStation 3)
· 하드웨어 쉐이더 (Hardware shader)
o 쉐이더 모델 2b (Shader model 2b) 하위 호환
o 쉐이더 모델 3 (Shader Model 3) 완벽 지원
o 쉐이더 모델 4 (Shader Model 4) 완벽 지원
§ 지오메트리 쉐이더 (Geometry Shader) 완벽 지원
o 쉐이더 모델 5 (Shader Model 5) 곧 지원 예정
· 인도어 (BSP, CSG, PVS, Occlusion Culling, Static Meshes, Voxel Spaces)   
· 아웃도어 (Height-mapped Terrain, Voxel Terrain)  스크린샷은 구버전으로 최신버전은 이것보다 훨씬 향상되었다.
· 라이팅 & 쉐도우
o High Dynamic Range Rendering/Lighting 
o Per-pixel Diffuse/Specular Lighting 
o Volumetric Lighting
o Stencil Dynamic Shadow Volumes
o Ultra Extreme Multi-sampled High Quality Soft Shadows
o Dynamic Fuzzy Soft Shadows  
o Self-shadows
o Geomerics의 Enlighten이 통합되어 있다. (Unreal Engine 3 IPP)
§ Global Illumination/Radiosity를 구현하고 재반사의 의해 생성되는 Soft Shadows를 실시간으로 구현하는 미들웨어 라이팅 엔진이다.
· 텍스처링
o Alpha Blending
o Light Mapping and Shadow Mapping
o Multi-pass Normal-mapping (Normal map/Bump map/Specular map/gloss map/opacity map/heghit map/offset map/etc)   
o Virtual Displacement Mapping (가상 변위 매핑)
o Real Dispalcement Mapping (변위 매핑)
o Allegorithmic의 ProFX가 통합되어 있다. (Unreal Engine 3 IPP)
· 애니메이션
o Vertex Animation with LOD (Keyframed, Inverse Kinematics, Motion Captured)
o Skeletal Animation with LOD (Animated, Motion Captured)
o Smooth-skinned Geometry with LOD (소프트웨어 및 하드우어 스키닝)
o Animation Blending with LOD (애니메이션 혼합과 LOD)
o OC3 Entertainemtn의 FaceFX가 통합되어 있다. (Unreal Engine 3 IPP)
§ Facial Animation & Lipsync를 구현하는 미들웨어 애니메이션 엔진이다.
· 특수 효과
o Depth-of-field (촛점 심도)
o Motion-blur (모션 블러)
o Distance Fog (Radial, Plane)
o Height Fog (높이 안개)
o Volume Fog (입체 안개) 
o Frame Buffer Distortion (수면 효과나 왜곡 효과 등)  
o Multiple Sky-Box (다중 스카이 박스)
o 기타 등등
· 파티클 시스템
o 일반 파티클
§ 탄피, 불꽃 등
o 소프트 파티클
§ 불, 물, 연기, 모래, 비, 눈, 우박, 안개 등
o Volumetric 파티클
§ 입체로 된 구름, 불, 물방울, 안개, 폭발효과 등을 구현
· Voxel 오브젝트
· 초목 구현
o IDV의 SpeedTreeRT가 통합되어 있다. (Unreal Engine 3 IPP)
· Split-screen rendering (화면 나누기)
· 고해상도 스크린샷 지원 (다양한 이미지 포맷)
물리 엔진
· 멀티 쓰레딩 물리 연산 지원
· Ageia의 PhysX 물리 엔진이 통합되어 있다. (Unreal Engine 3 IPP)
o UnrealScript와 연동되어 있고 UnrealEd 내부에 강력한 물리 컨트롤 툴셋으로 통합되어 있어서 UnrealEd 내에서 곧 바로 사용 가능하며 기본적으로 엔진의 모든 요소들과 연동되어 있어서 특별히 연동 작업이 필요 없다.
· ?RagDoll Physics (봉제인형 물리효과) 지원 
· 모든 오브젝트에 물리적인 충돌 및 감지와 중력효과 지원
· Vehicles Physics (탈것에 물리효과 지원)
o Ageia의 PhysX 물리 연산 하드웨어도 지원한다.
o 물리 + 애니메이션 + 사용자 입력 반응을 지원한다.
· 물리 기반 사운드 지원
o PhysX의 지속적인 업데이트.
o PhysX의 성능은 동명의 PhysX 하드웨어에 기반하면 엄청난 성능을 자랑하지만 소프트웨어적으로 돌아가는 물리 엔진의 성능은 현재 4.0 버전까지 나온 Havok Physics보다 못한 것 같다. 때문에 언리얼 엔진 3를 사용하는 개발사 중에는 언리얼 엔진 3에 무상 제공되는 PhysX 대신에 Havok 물리 엔진을 사용하는 개발사들도 상당수가 있다.
§ 다른 물리 엔진을 PhysX와 혼용하거나 또는 완전히 교체가 가능하며 이런 작업은 매우 간단한 편이다.
인공지능 엔진
· 멀티 쓰레딩 인공지능 연산 지원
· 싱글 플레이 기반의 다양한 A.I. 구현 시스템
· 싱글 플레이에서 동료의 A.I.에 대한 구현 시스템
· 길찾기 구조, BOT A.I., 탈것의 A.I., 팀기반 운용 A.I., 상황 판단 능력등을 보유한 인공지능 엔진
o 빠른 FPS에 최적이지만 길찾기 구조는 MMOG에서도 유용하게 쓸 수 있다.
· Engenuity의 AI Implant이 통합되어 있다. (Unreal Engine 3 IPP)
o 게임에 필요한 다양한 인공지능 기술을 제공하는 고급 미들웨어로서 많은 게임들에 채용되고 있는 인공지능 엔진이다.
· Kynogon의 Kynapse A.I.이 통합되어 있다. (Unreal Engine 3 IPP)
o 3D 월드의 패스 파인딩 (길찾기 구조)에 최적화 된 상용 인공지능 미들웨어이다.
· 두가지 이상의 인공지능 엔진을 언리얼 엔진 3내에서 동시 사용 가능하며 커스터마이징 혹은 필요하다면 외부의 다른 솔루션이나 직접 개발한 모듈을 동시 적용이 가능하다.
사운드 엔진
· 멀티 쓰레딩 사운드 처리
· Creative의 OpenAL (프리 소프트웨어)
o 돌비 서라운드 사운드,
o 모노 및 스테리오와 다중 채널 지원 (4.0, 5.1, 6.1, 7.1)
· OGG Vorbis (프리 소프트웨어)
o SIMD 최적화
· 크로스 플랫폼 DSP 효과 (반전, 기타)
· 마이크로폰 인풋
· 3D Positioning (3D 위치 기반 사운드 포지션), Spatialization (소리 떨림, 울림, 왜곡 효과), Doppler Shift (도플러 이동 사운드 효과)
네트워크 엔진
· 빠른 FPS 액션 게임에 최적화된 64명 기반의 서버-클라이언트 네트워크 엔진
o A.I.들의 무빙, 탈것, 물리 모델과 충돌 감지 예측의 빠른 네트워크 기능을 모두 포함한다.
· TCP 서버 네트워크 엔진
· UDP 기반의 네트워크 엔진
· Quazal Technologies의 Rendez-Vous이 통합되어 있다. (Unreal Engine 3 IPP)
o PC용 고급 상용 네트워크 엔진이다.
· Quazal Technologies의 Spark이 통합되어 있다. (Unreal Engine 3 IPP)
o 콘솔용 고급 상용 네트워크 엔진이다.
· MMO용 서버-클라이언트 네트워크 엔진은 아직까지는 제공되지 않는다.
로컬라이징 유니코드
· 16비트 유니코드를 지원하며 기본적으로 한국어, 중국어, 일본어를 포함한 총 9개국어가 지원되며 여러가지 폰트가 제공된다. 
무비 레코딩 기능
· 엔진을 통한 실시간 Demo 녹화 기능이 기본적으로 제공되며 이 기능은 다양하게 커스터마이징이 가능하다.
· DivX 동영상 포맷으로 동영상 아웃풋 레코딩이 가능한 기능이 기본적으로 통합되어 제공된다. (DivX 녹화 기능을 사용하는 것은 별도의 라이센스 비용이 필요 없지만 DivX 라이브러리를 제공 받기 위해서는 직접 DivX에 라이센스 해야 한다)
· RAD Game Tools의 Bink Video가 통합되어 있다. (Unreal Engine 3 IPP)
o 언리얼 엔진 3상에 통합되어 있으며 별도의 비용이 필요 없이 Bink Video에서 제공하는 다양한 포맷으로 녹화와 재생 및 편집이 가능하다.
Unreal Developer Network (UDN) 개발자 네트워크 지원
· 언리얼 엔진 2와 마찬가지로 UDN에서 개발자들끼리 다양한 정보나 기술, 소스 코드, 커스텀 유틸리티, 커스텀 툴셋, 커스텀 이펙트등을 공유 가능하다.
· 에픽에서 공식적으로 제공하는 예제 및 샘플등이 제공된다.
· 언리얼 엔진 3에 기본적으로 없는 다양한 종류의 렌더링 기술 및 기타 기능, 새로운 툴들을 추가적으로 지원 받을 수 있다.
o 자체적으로 커스터마이징을 하는 것 외에 엔진에 기본적으로 없는 기능들을 선택적으로 골라서 지원받아 커스터마이징 하는 것이 가능하다.
o 이것은 현재 지속적으로 추가 중이며 차후 언리얼 엔진 3.5와 함께 많은 내용들이 업데이트 될 것이다.
Unreal Engine 3 Integrated Partners Program (Unreal Engine 3 IPP)
· Unreal Engine 3 Integrated Partners Program은 에픽 서드 파티들이나 외부의 훌륭한 미들웨어들을 언리얼 엔진 3상에 통합하는 것을 의미하며 줄여서 Unreal Engine 3 IPP라고 부른다.
o 외부 미들웨어는 렌더링 관련, 네트워크 관련, 물리, 애니메이션, 인공지능 관련등 각 분야의 뛰어난 기술을 언리얼 엔진 3를 사용하는 개발사들에게 제공하기 위한 목적이며 새로운 미들웨어가 Unreal Engine 3 IPP로 계약이 되면 곧바로 언리얼 엔진 3의 다음 버전업에 포함이 되며 이 Unreal Engine 3 IPP로 추가된 미들웨어는 언리얼 엔진 3의 기본적인 기능으로 자리잡게 되므로 각 미들웨어 개발사에게 별도의 라이센스 비용을 들일필요가 없이 언리얼 엔진 3를 사용함으로서 해당 미들웨어들을 모두 무상으로 사용할 수 있다.
· IPP 체결이 된 미들웨어들은 단순히 언리얼 엔진 3에 번들로 제공되는게 아니라 언리얼 엔진의 코어 시스템 기반하에 엔진과 완전하게 융합되서 제공된다.
o Unreal Engine 3 IPP로 제공되는 모든 미들웨어들은 UnrealScript를 통해서 제어할 수 있으며 UnrealEd상에서 곧바로 사용할 수 있다.
o Unreal Engine 3 IPP로 제공되는 모든 미들웨어들의 툴은 UnrealEd상의 내부 툴로 포함되며 UnrealEd 내의 여러 툴간의 각 상호작용이 이루어진다.
o Unreal Engine 3 IPP로 제공되는 모든 미들웨어들은 언리얼 엔진 3의 모든 기능들과의 상호 연동은 물론 Unreal Engine 3 IPP로 포함된 모든 미들웨어들간의 상호 연동이 가능하게 된 상태로 제공이 된다.
o Unreal Engine 3 IPP에 포함되는 모든 미들웨어들은 항상 최신버전을 유지하기 위해서 해당 미들웨어의 최신버전이 릴리즈되면 언리얼 엔진 3의 최신버전에 해당 미들웨어의 최신버전을 Unreal Engine 3 IPP에 업데이트 한다.
· Unreal Engine 3 IPP에 현재까지 포함된 리스트는 다음과 같다
o Ageia의 PhysX (언리얼 엔진 3가 라이센스 되기 전부터 포함)
§ 물리 엔진 SDK, 툴셋
§ 물리 엔진은 언리얼 엔진 3와 완전하게 융합
§ 기본적인 물리현상, 탈것, 파티클등의 특수효과에 적용 가능하게 융합
§ 툴셋은 UnrealEd 상의 PhAT라는 물리 셋팅 툴의 일부로 완전하게 통합
o OC3 Entertainment의 FaceFX Technology (2005년 10월 18일부터 포함)
§ 페이셜 애니메이션 및 립싱크 엔진 및 툴셋
§ 페이셜 애니메이션과 립싱크 엔진의 기능은 언리얼 엔진 3상에 완전하게 통합
§ 툴셋은 UnrealEd상에 완전하게 통합
o RAD Game Tools의 Bink Video (2006년 초에 포함)
§ 동영상 녹화, 편집 및 재생하는 미들웨어
o Quazal Technologies의 Rendez-Vous (2006년 9월 28일부터 포함)
§ 네트워크 엔진
o Quazal Technologies의 Spark (2006년 9월 28일부터 포함)
§ 네트워크 엔진
o Fonix Speech의 VoiceIn (2006년 10월 2일부터 포함)
§ 음성 인식 엔진
o Engenuity의 AI Implant (2006년 10월 3일부터 포함)
§ 인공지능 엔진
o IDV의 SpeedTreeRT (2006년 10월 5일부터 포함)
§ 초목 구현 시스템
§ 언리얼 엔진 3상에 통합된 물리 엔진이나 라이팅 엔진을 통해서 새로운 라이팅과 쉐도우를 적용하거나 물리 현상을 적용이 가능
§ IPP로 포함되기 전에는 언리얼 엔진 3를 구입하고서도 SpeedTreeRT를 따로 비용을 들여서 라이센스 해야했지만 지금은 IPP에 포함되어서 비용을 따로 들일 필요 없이 언리얼 엔진 3에서 SpeedTreeRT를 사용할 수 있다
o Digimask의 Digimask SDK (2006년 11월 1일부터 포함)
§ 게이머가 직접 자신의 얼굴을 게임 속 캐릭터의 얼굴로 만들 수 있는 안면 모델 생성 및 적용 솔루션
§ 머리 모양과 얼굴의 텍스처를 생성하고 적용이 가능
§ Unreal Engine 3 IPP로 포함된 미들웨어인 FaceFX와 연동해서 사실적인 페이셜 애니메이션과 립싱크가 가능
§ Unreal Engine 3 IPP로 포함된 미들웨어인 VoiceIn까지 연동해서 게임중에 자신의 음성을 이용해 실시간 립싱크도 적용이 가능
o Kynogon의 Kynapse A.I. (2006년 11월 2일부터 포함)
§ 인공지능 엔진
o Geomerics의 Enlighten (2007년 2월 19일부터 포함)
§ 실시간 글로벌 일루미네이션 (Real-Time Global Illumination), 래디오시티 (Radiosity) 라이팅 및 재반사에 의해 생성되는 부드러운 그림자 (Soft Shadows)를 구현하는 엔진 차세대 라이팅 엔진
o Allegorithmic의 ProFX (2007년 3월 1일부터 포함)
§ Flexibility하게 절차적 텍스처 (Procedural Textures)를 생성하여 Shading 처리하는 차세대 텍스처 렌더링 엔진
 
토론
언리얼 엔진 3는 Direct3D 9b의 Shader Model 2x를 최소사양으로 하며 Shader Model 2.0 및 그 이하의 고정 파이프라인 렌더링과의 하위호환성을 완전히 배제한 완전한 Shader Based Rendering을 추구합니다. 그 이하는 고려되지 않기 때문에 그 이하를 염두하는 게임이라면 언리얼 엔진 3를 선택하지 말고 언리얼 엔진 2나 다른 엔진을 선택하는게 옳습니다. --아무개
쉐이더 3.0 이상아닌가요? PC로 나온 레인보우식스 베가스,RoboHordes 데모를 보면 Radeon x800 시리즈도 지원을 하지 않습니다(분명히 x800은 쉐이더 2.x 이상을 지원하는데).그리고 언리얼 엔진 3에 대한 단점은 전혀 없군요. 2를 써본 경험으론 그렇게 유연한 엔진이란 생각은 들지 않던데요. --다른 아무개
UE2는 프로젝트에 쓴게 아니지만 소스만 봤는데 UE3에 비해서는 유연함이 많이 떨어지는 편입니다. 하지만 그전부터 언리얼 엔진 시리즈가 유연함에서 좋다고 한 이유는 다른 통합형 엔진에 비해서 구조가 이쁘기 때문입니다. 적어도 제가 본 엔진중 시리어스 샘과 파 크라이와 리스텍중에선 UE2가 코드의 상태가 잘 정리되있고 코드 내부도 modify하기에 편리한 점이 있죠.(예: core(UE써봤다면 뭔지 아시겠죠)에서 구현하는 uvm으로 모든 코드가 들어온 다음에 다시 분산시키고 engine에 핵심적인 기술이 들어있고 나머지 세부 객체를 파생시켜서 연동하는 형태와 모듈 객체마다 패키지 파일을 써서 언리얼 스크립트로 컨트롤하거나 텍스트 파일을 일부 떨궈서 텍스트 편집기로만 간단하게 설정할수 있는점을 들수 있겠네요. UE3도 같은 구조에서 더 광범위하고 편하게 쓰도록 확장되어 있습니다.
UE3의 단점을 들라면 글쎄요. 지금까지 단점은 특별히 느끼지 못했지만 저마다 뭔가 불만을 토로할수는 있겠죠. 저는 단점은 못느꼈습니다. 굳이 단점을 하나 말하라면 전 일주일마다 한번씩 업데이트 받아야한다는 강박관념(?)정도? 아니 업데이트 한다고 개발에 큰 차질이 생기는것도 아니고 별로 상관은 없지만. 그 외엔 개발중 생기는 트러블과 어려움은 어떤 엔진을 써도 마찬가지고 그런 이유가 UE3로 인해 생긴다고 생각되지 않으니까요. 예전부터 상용 엔진이나 미들웨어를 도입하는 사람들이 불만을 토로하는 경우를 종종봐왔는데 그런 경우는 "엔진에서 뭐든 그냥 알아서 다 해주겠지"라는 안일한 태도를 갖고 있는 사람들이거나 실력 부족인 경우가 대부분이었고 그렇게 불만 갖는 친구들 치고서 프로젝트가 순조로운걸 못봤습니다. 이건 물론 저만의 시각일수도 있지만 적어도 제가 봐왔던건 그렇습니다.
마지막으로 UE3는 쉐이더 2.x부터 돕니다. 레인보우 식스 베가스나 로보뭐시기는 안해봤지만 그 게임들이 지원안하는건 그 게임들 문제겠지요. 쉐이더 2.x를 지원하는 라데온 9xxx 시리즈부터 돌고 지포스는 쉐이더 3.0 지원하는 6xxx부터 제대로 돕니다. 다만 최신 드라이버는 쉐이더 3.0 기준으로 개발되고 있고요. 현재는 쉐이더 4 기반 드라이버가 준비되서 제공될려고 하고있습니다.
--UE3쓰는이
지금은 회사에서 나왔지만 얼마전까지만 해도 UE3를 쓰던 사람입니다. 저도 UE3 쓰면서 단점을 굳이 찾으라는 말은 억지로 단점이라고 짚어내지 않고서야는 이 엔진의 단점을 말하기란 다른 일로 생긴 문제를 억지로 엔진의 탓으로 핑계대는 것이라고 밖에 생각되지 않습니다. 뭐 팀원이나 팀장등의 입장으로서는 그런 발언 한마디에 자기들 밥줄이 걸린 문제니까 이해할만하지만요. --도타쿠
맞습니다. 언리얼 엔진 3 좋기만 하더만요. 엔진에 핑계대는 사람치고 자기 실력에 문제가 없는 사람없었다는... 언리얼 엔진 2까지는 조금 그렇지만 언리얼 엔진 3에는 투정부릴게 전혀 없습니다. 게임브리오만 쓰다가 언리얼 엔진 2, 3 구경해보니까 2는 좀 그래도 3는 차원이 다르더군요. 언리얼 엔진 3 쓰면서 느낀점은 이 대단한 엔진을 쓰면서 엔진에 핑계대는다는것은 프로그래머로서의 자질부터 스스로 체크해봐야겠다는 생각뿐이었습니다. --낙잠자자
정책이 저렇게 바뀐건 알지만 1년 유지보수에 $100,000인가요? --- 도타쿠
HDR 구현에 관심이 많은데, 라데온 9xxx 씨리즈에서도 된다니 신기하네요, 라데온 9xxx 씨리즈는 A16B16G16R16F에서 alpha blending을 지원하지 않는 걸로 알고 있습니다. 언리얼 엔진 3기반 게임은 pix로 잠깐 잡아보면 백버퍼가 A16B16G16R16F 인거 같던데 여튼 신기하군요 --- 지나가던이의 의견
HDR 구현에 대한 자료들은 인터넷 검색을 통해서 보시면 많은 자료들을 찾으실 수 있을겁니다. 파크라이 (크라이 엔진 1)는 HDR 구현을 SM2 이하에서는 구현하지 못했으나 나중에 나온 스플린터셀 3,4 시리즈나 언리얼 엔진 3, 하프라이프 2의 소스 엔진을 비롯해 요즘엔 SM2에서도 HDR을 구현합니다. 언리얼 엔진 3에선 SM3 이상 모드에서는 R16 G16 B16 A16모드나 R32 G32 B32 A32로도 HDR을 구현하는것도 가능합니다. 지포스 7000 계열 카드에서는 올 R32 G32 B32 A32모드로 돌리면 FSAA나 지연 렌더링을 사용 불가하게 되지만 이후의 카드에서는 가능합니다. --- UE3 사용자 의견

Posted by 노을삼킨별
,

알로샤의 번역 블로그 - 니시가와 젠지 번역 블로그
http://allosha.tistory.com/


니시가와 젠지의 캡콤의 게임엔진 MTFW(MT 프레임워크) 2.0
http://blog.naver.com/saebaryo?Redirect=Log&logNo=30049460381




Posted by 노을삼킨별
,

아래에서 HDR 관련 자료 가져옴 41~51
http://allosha.tistory.com/41

2008-12-05 13:33:00
ゲームグラフィックス、コンピュータグラフィックスは共に、ディスプレイ装置に表示することが前提であったために、レンダリングパイプラインが、この「ディスプレイ基準」で組み立てられて来た経緯がある。ところが ...... >> Read more

(C) Mainichi Communications Inc. All rights reserved.



게임 그래픽스와, 컴퓨터 그래픽스는 모두, 디스플레이 장치에 보여지는 것이 전제(前提)되어 있기 때문에, 렌더링 파이프라인이, 이 「디스플레이 기준」으로 만들어져 왔다. 그런데, 현실 세계를 리얼하게 표현하려고 할 때는, 이것은 족쇄가 된다. 이 족쇄를 끊으려는 움직임을 근대 실시간 3D그래픽스에서도 볼 수 있다.

그것이 「하이 다이나믹 레인지 렌더링」(HDR 렌더링:High Dynamic Range Rendering)이다

본래는 학술적인 연구테마로서 성행했었던 「HDR」이라는 키워드가, 지금은, PC에서의 실시간 렌더링 뿐 아니라, 가정용 게임기의 그래픽스 표현으로서도 표준이 되고 있다.

이번회부터는 당분간, 이 HDR 렌더링이라는 테마에 대해서 보도록 하자.


HDR렌더링이란?

원래 HDR 렌더링이라는 것은 어떤 의미가 있는 것일까? 우선은 여기로부터 해설해 나가자.

「HDR 렌더링」의 정의로서는 「표시에 이용하는 디스플레이 기기의 휘도(밝기 또는 선명도 )/색역(색상 범위)의 한계에 얽매이지 않고, 폭넓은 휘도/색역으로 렌더링을 수행하는 것」이 된다.

현재의 PC에서 일상적으로 취급하는 색 표현은 RGB(빨강, 녹색, 파랑)의 삼원색이 각각 8비트 정수로 표현된 24비트 컬러, 즉 1,677만개의 색이 가장 친밀한 색 표현이라 할 수 있다.

이 디스플레이에 채용되고 있는 「1,677만개 색」이라는 것은, 인간이 한번에 눈으로 감지할 수 있는 휘도나 색의 대략적인 범위를 RGB 삼원색(빛)을 각각 256단계(0~255)로 해서 표현한 것이다(R256×G256×B256 = 16,777,216). 인간이 한 번에 볼 수 있는 휘도범위/색범위는 이 1,677만 단계의 분해능(分解能, 해상도)으로 표현하는 정도면 거의 필요충분하다고 하지만, 현실 세계를 그대로 재현하는데는 불충분한 상황이 온다.

아래 그림은 인간의 시각을 알기 쉽게 도해한 것이다.

시각(視覺)의 다이나믹 레인지. 간상체(간상세포, rod cell)는 어두운 빛을 느끼기 위한 시각 세포. 그리고 원추체(원추세포, cone cell)는 밝은 빛이나 색을 느끼는 시각 세포. 아래의 수치의 단위는 루미넌스(luminance)값(lum/m2)

<그림 설명>
(제목) 시각의 다이나믹레인지
달(月) 없음、달밤、여명、방안、햇빛
원추체、도(밝기 또는 선명도)


예를들면, 「태양광을 반사하는 설원(1E+6=106)」은 상당히 밝고, 「별이 떠있는 밤하늘이 지표를 비추는 밝기(1E-6=10-6)」는 상당히 어두운 것을 상상 할 수 있을 것이다. 이 밝기나 어두움은 루미넌스값(lum/m2)에서는 실제로 10의 12승(1012)의 "격차(간격)"가 있다.

3D그래픽스는 루미넌스값으로 렌더링 하는 것은 아니지만, 그래도 현실 세계의 밝기와 어두움을 에너지로서 기록하려면 터무니없는 표현영역이 필요해 진다는 것은 상상할 수 있을 것이다.

앞에서 말한 설원과 밤하늘의 예가 현실 세계의 최대의 밝기와 어두움을 나타내고 있다고 가정하면, 이것을 수치로 표현하려면 1012의 범위를 표현할 수 없으면 안된다는 것이다.

이야기를 알기 쉽게 하기 위해서, 「취급할 수 있는 수치의 범위」라는 의미에 있어서의  "다이나믹 레인지"를 계산해 보자 (용어의 정의로서의 "다이나믹 레인지"라는 건 표현할 수 있는 최소값와 최대값의 대비를 나타내는 단위이다).

Log를 취해 10을 곱한 것을 dB(데시벨)이라고 하는데, 1012의 표현폭이 필요하다는 것은 120dB의 다이나믹 레인지가 필요하다는 것이 된다.

이른바 1,677만개의 색은 휘도를 8비트 정수, 즉 256 단계로 기록하는 방식이므로 256≒102.4  가 되어, 약 24dB 만큼 밖에는 기록할 수 없게 된다. 그래서, 현실 세계(120dB)의 약 40억분의1(≒1012÷102.4) 만큼 밖에는 기록할 수 없다는 것이다. 물론, 1012의 폭의 계조(階調, 단계)를 8비트의 256 단계로 무리하게 표현하는 것도 가능은 하지만, 이것은 분해능(分解能, 해상도)으로서는 너무 엉성하다.

이 광대한 다이나믹 레인지의 밝기, 어두움을, 가능한 정확하게 기록하려고 하는 것이 HDR 렌더링의 기본적인 접근(법)이다. (계속)


현실세계를 기록하는데 필요한 다이나믹 레인지란?

거기서, 다이렉트X 9 세대 / SM2.0 대응 GPU의 시대에, HDR 렌더링을 실현하는데 필요한 120dB 정도의 수치표현  능력을 가진 프레임 버퍼 포맷으로 제공된 것이, 각 RGB가 16비트 부동소수점(FP16)으로 표시되는 포맷이다.

FP16은 부호 1비트, 가수10비트, 지수 5비트로 구성되는 부동 소수점 포맷이다. 표현 범위는 (2의 10승)×(2의 32승) ≒ 4.4×10^12, 즉 다이나믹 레인지가 120dB이 되어 (지수5비트=2^5, 즉 32), 다이나믹 레인지로서 현실 세계의 상당한 비율의 휘도 범위를 기록할 수 있는 잠재력이 있다는 것을 알 수 있다.

컴퓨팅 세계에서 부동 소수점 표현이라 하면, 「단정도(單精度, Single-precision, 1개 메모리 단위로 수치를 저장)」라고 불리는 32비트 부동 소수점(FP32)과, 「배정도(倍精度, Double Precision, 2개 메모리 단위로 수치를 저장 )」라고 불리는 64비트 부동 소수점(FP64)이 일반적이지만, 이 FP16은 주로 그래픽스용으로서 제창된 것이다. 덧붙여서, 이 제창자는 SF영화 「스타워즈」의 특수 효과로 친숙한 ILM(Industrial Light & Magic)의 CG파트(팀)이고, FP16은 이 ILM이 제창한 HDR영상의 오픈소스 규격 「OpenEXR」안에 정의되어 있다.

단정도(1배 정밀도) FP32는 부호부 1비트, 지수부 8비트, 가수부 23비트로 구성되는 부동 소수점 포맷이므로, 표현 범위는 (2의23승)×(2의256승) = 9.7×10^83이며, 다이나믹 레인지는 830dB로, 이쪽은 반대로 지나치게 오버스펙이 나 버린다.

그러한 내막이 있어서, 최신세대의 GPU에서는, αRGB가 각각 FP16으로 표시되는 64비트 버퍼(16비트×4, 이하 FP16-64비트 버퍼)가, 실시간 3D그래픽스용, 3D게임용의 표준 HDR 렌더링용 버퍼 포맷으로 되어 있다.

단, 종래의 αRGB가 각각 8비트 정수인 32비트 버퍼(이하, int8-32비트 버퍼)와 비교하면 2배의 비디오메모리를 소비하기 때문에, 32비트 버퍼(int8-32비트 버퍼)와 동등한 퍼포먼스를 보이려면 단위시간 당 대역이나 버스 성능은 2배가 요구된다.

이것은 다이렉트X 9 세대 / SM2.0 대응 GPU 때에는, 부하가 상당히 심한 것이었지만, 최신세대의 다이렉트X 10  세대 / SM4.0 대응 GPU에서는, 현실적으로 문제 없이 활용할 수 있는 솔루션이 되고 있다.


HDR렌더링의 메리트란?

HDR 렌더링은, 현실 세계에 가까운 다이나믹 레인지 정보를 거의 그대로 기록해 나가는 렌더링 기법이라는 것이라는 것은 상상할 수 있을 것이라 생각한다.

그렇다고 해도 「그것이 대체 어쨌다는 거냐?」라는 의문도 들지 모르겠다. 하이 다이나믹 레인지로 렌더링 했다고는 해도, 표시해주는 디스플레이 장치는 종래의 1,677만 색으로 디스플레이하기 때문에, 그대로는 표시될 수 없다.

FP16-64비트의 HDR버퍼에 HDR 렌더링을 했지만 결국, 표시할 단계에서 int8-32비트에 해당되는 LDR버퍼에 뭉쳐넣어서 감색처리해야 하는 것이다.

그렇게 되면, HDR렌더링의 메리트는 도대체 어디에 있는가?

그것은, 주로, 지금부터 소개할 3가지 요소에 있다.

(1)음영이 보다 리얼해 진다. (2)노출 시뮬레이션을 할 수 있다. (3)눈부심 표현이 가능하다.


HDR렌더링의 첫번째 효능 ~ 음영이 보다 리얼해 진다

HDR렌더링의 첫번째 효능은, 음영이 보다 리얼하게 된다는 것이다.

예를 들면, 빛을 별로 반사하지 않는 재질로서 도로 노면의 아스팔트를 예로 들어 보자.

도로의 재질로 알려진 아스팔트의 빛의 반사율은 불과 7%정도라고 한다. 이것은 「거의 반사하지 않지만, 전혀 반사하지 않는 것도 아니다」라고 할 만큼의 반사율이다.

예를 들면, 일반적으로 유통되고 있는 αRGB 각각 8비트 정수의 1,677만 색모드에서, 8비트 정수 표현으로 가장 밝은 255의 7%는 약 18이 된다. 즉 RGB가 모두 255인 백색광(白色光)으로 라이팅 해도, 그 음영 처리 결과는 RGB가 모두 18 이라는 상당히 어두운 색, 즉 계조(단계)가 되어 버리는 것이다.

8비트, 즉 256 계조(단계)에서 18은 이렇게 어둡다(이미지)

그러나, 현실 세계에서는, 고휘도의 태양광을 받은 노면은 둔하게 빛나 보인다.

이것은 3D그래픽스로 옮기면, 광원인 태양이 디스플레이로 표현되는 최고 휘도인 RGB = 255이 아닌, 터무니 없이 높은 휘도값을 가지고 있기 때문이다.

HDR 렌더링은, 이러한 「RGB가 모두 255로 한계점 도달」이라는 제약을 제거해 버리는 것이므로, 종래의 렌더링 기법에서는 아무리해도 묻혀 버리고 마는 음영을 부상(浮上) 시키는 효과를 기대할 수 있다.(계속)

 

「HalfLife2:Lost Coast」(VALVE,2005)로 부터. LDR 렌더링에서는 이와 같이 전체가 어둡다


  HDR광원을 사용한 HDR 렌더링에서는, 이와
  같이 파묻힌 음영이 보이게 된다. 그림으로서
  는 전체가 밝아진 것 같은 느낌이 든다. 덧붙여
  「HalfLife2:Lost Coast」에서는, 렌더링에
  FP16-64비트 버퍼를 렌더타겟으로 사용했다



HDR렌더링의 두번째 효능 ~ 노출 시뮬레이션을 할 수 있다

첫번째 효능은, 묻혀 있는 낮은 반사율의 음영이 보이게 된다……라고 하는 알기 쉬운 예였지만, 반대로, 높은 휘도의 광원에서 고반사율의 재질(금속과 같은 거울반사등)에서는 당연히, 음영처리 결과는 디스플레이 표시색의 범위를 넘는 색(휘도)이 되어 버린다. 현재, 일부의 메이커로부터 하이 다이나믹 레인지 디스플레이 장치라는 것이 발매되기 시작했지만, 아직은 고가이고 일반용은 아니다.

따라서, HDR렌더링에 의해서 생성된 프레임(영상)을, 일반적으로 보급되어 있는 디스플레이에서 표시하려면, 아무래도 「표시하기 위한 조정(≒감색)가공 처리」가 필요하게 된다. 이 처리계(處理系)는 「톤 매핑」(Tone Mapping)이라 불리고, 넓은 의미로는, 이 처리를 포함해 HDR 렌더링이라고 부르기도 한다.

구BRIGHTSIDE사(현재는Dolby가 매수)가 개발하고,  日商 Electronics사가 발매한 HDR 디스플레이 「DR37-P」. contrast比가 20만:1, 최대 휘도는 3000cd/평방미터


그런데, 앞에서 나왔던 「시각의 다이나믹 레인지」그림에서와 같이 인간이 지각할 수 있는 다이나믹 레인지는 80~120dB 전후라고 한다. 그러나, 실제로 한번에 색으로 지각할 수 있는 범위는, 보고 있는 씬의 최대 휘도, 혹은 평균 휘도에 끌려가 버린다.

예를 들면 이런 경험이 있을 것이다. 휴대 전화를 실내에서 열었을 때는 액정화면이 잘 보이는데, 맑은 날의 옥외에서 열면 거의 안보인다. 휴대 전화의 액정 디스플레이의 밝기는 변함이 없는데, 보일 때와 안보일 때가 있다.

이것은, 인간의 눈에 있는 광채(Iris)라고 하는 부위가, 보고 있는 정경 전체의 휘도에 적응해 닫히거나 열리거나 해서, 안구내로 통하는 광량을 조정하고 있기 때문에 일어나는 현상이다. 실내에서는 광채가 열려 망막에 빛을 많이 들어오므로, 액정화면의 빛도 밝게 보이지만, 옥외에서는 광채가 좁혀져 망막에의 광량이 줄어 들어, 액정화면 정도의 휘도에서는 망막에 닿기 어렵게 되는 것이다.

HDR렌더링에서는, 매우 어두운 휘도 영역으로부터 매우 밝은 휘도 영역까지의 음영을 올바르게 버퍼에 기록하고 있으므로, 그 씬의 평균 휘도를 구하고 나서, 그 값을 중심으로 톤 매핑을 실시하면 그 씬을 적정 휘도로 볼 수 있다. 물론, 적정 휘도 범위 이하의 계조는 죽어서 검은색으로 떨어지거나 범위 이상의 계조는 흰색으로 날아간다거나 해 버리지만, 실제 우리의 시각도 그렇기 때문에, 리얼한 시야를 재현한다는 의미에서는, 그것은 오히려 안성마춤이다. 이 톤매핑 공정은, 눈의 광채가 보기 편한 조리개로 보는 동작을 흉내낸 것과 같다.

덧붙여 이 톤매핑을 매프레임 단위로 순간에 실시하는 것이 아니라, 약간의 지연을 수반해서 서서히 실시하면, 한층 더 리얼리티가 증가한다.

현실 세계에서도, 어두운 방으로부터 밝은 옥외로 뛰어 나오면, 일순간 눈이 먼 눈부심에 싸이는데, 점차 눈이 익숙해져서 적정한 휘도 밸런스로 정경이 눈에 들어오게 된다.

이, 적정 휘도로 조정하는 동적인 톤매핑 처리를, 일부러 약간의 지연을 수반해 실시해 주면, 「밝기/어두움에 눈이 익숙해져 간다」라는 모습을 표현할 수 있다.

이러한 동적인 톤 매핑 처리는, 눈으로 말하면 광채, 카메라로 말하면 「조리개」제어에 해당되, 이러한 광량 조정에 의한 명암의 컨트롤은 카메라 용어에서 말하는 「노출 보정」에 가깝다.

HDR렌더링의 두번째 효능은, 이러한 노출의 시뮬레이션을 실시할 수 있다는데 있다.(계속)

 

「HalfLife2:Lost Coast」(VALVE,2005)로 부터. 어두운 옥내에 눈이 익숙해져 있는 상태.

  이대로 옥외에 뛰어 나오면 옥외의 정경이
  눈부시게 보인다.

 

점차 눈이 익숙해져 오지만 안쪽의 석벽의 하이라이트는 역시 눈부시게 보이고 있다.

  겨우 석벽의 하이라이트의 음영이 올바르게
  보여 온다. 이것으로 옥외에 노출이 적정한
  상태가 되었다



HDR렌더링 세번째 효능 ~ 눈부심의 표현이 가능해 진다.

인간의 눈이나 카메라로 고휘도의 것을 보면, 그 빛이 포화되어 보이는 시각 효과를 얻을 수 있다. 이것은, 비록 앞에서 말한 노출 보정이 제대로 되고 있어도, 극단적으로 밝은 곳은 그렇게 보인다.

또, 그 고휘도인 물체의 모습의 대부분이 차폐물에 차단되 있어도, 고휘도 물체가 일부라도 차폐물로부터 머리를 내밀고 있으면, 차폐물과의 전후관계를 초월해, 강한 빛이 이쪽으로 새어 나오는 것처럼 보인다.

친밀한 예로, 여름 낮에, 나무 그늘에서 하늘을 올려다 보면, 가지나 잎 너머로 태양을 보면, 가지나 잎의 차폐를 넘어서 빛이 확하고 방사상으로 퍼져 보이는 체험을 했을 것이다.

이것은 극히 휘도가 높은 빛이 렌즈 안에서 반사된 결과이거나, 눈의 첩모(속눈썹)에서 빛이 회절(回折) 하거나(꺾이거나) 하는 현상이, 그렇게 보이도록 만들고 있는 것이라고 한다.

HDR렌더링의 세번째 효능은, 이런 고휘도 부분의 표현을 고안하는 것으로, 영상으로서의 리얼리티, 인간의 시각으로서의 리얼리티를 연출할 수 있다는 점에 있다.

다만, 이런 "눈부심"의 표현은, 광학 현상을 올바르게 시뮬레이션하는 구현이라기 보다, 아티스트나 3D그래픽스 엔진 설계자의 센스에 근거해서, 화상 처리적인 접근으로 구현된 기법이 주류로 되어 있다.

가장 대표적인 것은, 고휘도 부분으로부터 빛이 희미하게 넘쳐나오는 표현으로, 이것을 특히 「Light Bloom」이라고 부르거나 한다.

또, 수면의 잔물결에 반사된 빛등이 방사상(放射狀)으로 빛이 포화되어(넘쳐) 나오는 표현은 「Glair」라고 나누어 부르거나 한다.(계속)

 

「DeusEx 2」(ION STORM,2004)으로 부터. 라이트 Bloom 없음.

  라이트 Bloom 있음. "있음"이면 씬의
  고휘도 부분으로부터 희미한 빛이 흘러
  나온 것 같은 부드러운 이미지의 영상이 된다


「3DMark06」(Futuremark,2006)으로 부터. Glair의 예. 호수면에 비친 태양으로부터의 고휘도 빛이 방사상으로 넘쳐 나오고 있다


HDR렌더링의 역사

HDR 렌더링의 기본적인 개념을 알았으니, 최근까지의 HDR 렌더링의 기술 동향을 간단히 돌아 보자.

앞절까지, HDR렌더링은 FP16-64비트 버퍼와 같은 부동 소수점 버퍼를 활용해 실시하는 것……이라는 논조로 해설해 왔다. 부동 소수점 버퍼 구현은 다이렉트X 9 세대 / SM2.0 대응의 GPU 이후 이므로, 그 전에는 HDR 렌더링의 구현은 없었을까? 라고 하면 실은 그렇지 않다.

그보다 조금 앞서서, 「의사적인 HDR 렌더링」이라는 접근방법이 3D게임에 실제로 구현된 예가 있다.

이 기술로 주목을 끈 것은, Xbox용 게임으로 발매된 「DOUBLE S.T.E.A.L」(분카사, 2002년)이다. 이 게임은 일본에서 개발 되었고, 유사 HDR 렌더링이었지만, 그 후의 진짜 HDR 렌더링의 구현에도 응용할 수 있는 많은 기초 기술을 확립했던 게임이였다.

Xbox의 GPU는 다이렉트X 8 세대 / SM1.x 대응이였고, FP16-64비트의 부동소수점 HDR버퍼는 없었기 때문에, 통상의 int8-32비트의 정수 LDR 버퍼를 독특하게 사용해 유사 HDR 렌더링 테크닉을 구현했다. 구체적으로는 αRGB 중, α성분에 8비트/256단계에서는 표현할 수 없는 고휘도 정보를 저장해서 실현했다.

이 유사 HDR 렌더링 기술에 대해서는 본연재의 후반부에서 해설할 예정이다.

「DOUBLE S.T.E.A.L」(분카사, 2002년)로 부터. 3D게임에서 HDR렌더링의 원조격인 존재

2002년 후반에, DirectX 9 세대 / SM2.0 대응 GPU가 등장해, 부동 소수점 버퍼가 지원 되었지만, 멀티샘플 안티-에일리어싱 (MSAA:Multi-Sampled Anti-Aliasing) 처리를 적용할 수 없다는 제약 때문에, 실제로는, 이후에도 당분간, 3D게임의 HDR렌더링은, 「DOUBLE S.T.E.A.L」적인 유사 HDR 렌더링 구현이 주류였었다.

2004년에 「본격적인 HDR 렌더링을 구현했다」라는 선전과 함께 등장한 3D게임 「HalfLife2」(Valve,2004)에서도, α값에 RGB에서 공통으로 이용하는 스케일치(배율치)를 넣고, 각 RGB 값을 X=X×α×16 으로 디코딩하는 유사 HDR 렌더링에 지나지 않았다. 그로 인해, 동적인 톤매핑은 구현되어 있지 않았고, 환경맵 등도 HDR 정보는 잘라 버리는 간이 구현이였다.

「HalfLife2」(Valve,2004)로 부터. 하늘의 태양은 HDR정보가 활용된 Bloom을 일으키고 있는데 수면에 비친 태양은 어두워져 있다. HalfLife2는 텍스처에는 HDR 정보가 반영되지 않았다

2004년에는 다이렉트X 9 세대 / SM3.0 대응 GPU가 발표되어 FP16-64비트 버퍼의 블렌딩이 지원되는 등, 그 실용성도 높아졌다. 이 영향으로 2005년 경이 되자 서서히 FP16-64비트 버퍼를 활용한 HDR렌더링을 구현한 3D그래픽스 엔진도 서서히 등장하기 시작했지만, 주류는 되지 못했다. 그것은, FP16-64비트 버퍼에 대한 안티-에일리어싱 처리에 대응한 GPU가 ATI(현AMD)의 Radeon X1000시리즈로 한정되었고 경쟁사인 NVIDIA의 GeForce 6000/7000 시리즈에서는 지원하지 않았기 때문이다.

2006년이 되자, FP16-64비트 버퍼를 활용한 HDR렌더링에 대한 연구가 진행되어, 이것을 구현한 3D게임 그래픽스가 많아졌다. 이것은, 2005년 말에 발매된 Xbox 360의 영향도 적지 않았다고 생각된다. Xbox 360의 GPU는 다이렉트X 9 세대 / SM3.0 대응의 GPU로, 3D게임 그래픽스의 설계가, 이 다이렉트X 9 세대 / SM3.0 대응의 GPU를 전제로 한 것이였기 때문이다.(계속)

「AGE OF EMPIRES III」(ENSEMBLE STUDIOS,2005)는 메인스트림인PC게임 작품으로서는 처음으로 FP16-64비트 버퍼에 의한 HDR 렌더링을 사용한 타이틀이 되었다



PS3의 GPU「RSX」는 NVIDIA의 GeForce 7800 GTX를 베이스로 설계되었기 때문에, FP16-64비트 버퍼에  대한 MSAA를 사용할 수 없는 제약 또한 계승해 버렸다. 코나미의 PS3 전용 타이틀 「메탈 기어 솔리드4」(2008년)에서는, 이 제약과 Fill Rate을 고려해, FP16-64비트 버퍼의 채택을 단념했다.

CEDEC 2008의 슬라이드로 부터.「메탈 기어 솔리드4」에서는 32비트 정수 버퍼의 HDR 렌더링을 사용했다.

<그림 설명>
좌:
HDR렌더링의 필요성(2)
당초에는 FP16 버퍼에 의한 렌러링
- Fill Rate이 부족하다
- MSAA를 사용할  수 없다.
- FP16 버퍼에 의한 렌러딩을 단념
우:
HDR 인코더 렌더링(1)
MGS4에서는 HDR 인코드 채용
- 32비트/픽셀의 Band Width에서 HDR을 실현
- HDR 인코드 렌더링에도 몇개의 문제점
  ㆍ휘도레인지, 정밀도 부족
      MGS4에서는 그정도로 문제는 되지 않았다.
  ㆍ반투명의 직접 렌더링이 불가능


또한, 약간 변칙적인 수법이지만, 「Half-Life2」의 개발사인 VALVE는, 이 FP16-64비트 버퍼에 대한 제약을 피하면서, FP16-64비트 버퍼에 대해서 HDR 렌더링을 하는 테크닉을 발표했다.

이 VALVE의 구현에서는, 텍스처나 렌더링을 FP16-64비트 버퍼로 하지만, Bloom 효과나 Glare 효과의 처리 전에 톤매핑을 실시해서 32비트의 LDR 버퍼로 해 버린다.

              VALVE가 구현한 타협안적인 리얼 HDR 렌더링

이 방법에서는, Blooming 효과나 Glare 효과를 처리하는 시점에서는 HDR정보는 완전히 잃게 되지만, LDR(RGB가 0~255)버퍼 속에서 고휘도 영역(예를 들면 RGB가 평균 240 이상 등)을 추출해, 거기서부터 Bloom 효과나 Glare 효과를 생성한다. 반사나 굴절한 정경(동적 생성되는 환경 큐브 맵 등)에 대해서도, 렌더링 자체는HDR 차원에서 행해져 그 씬에 적합한 톤매핑을 하기 때문에, HDR정보는 분명히 잃게 되지만, 뒷단계의 Bloom 효과, Glare 효과도 모순 없이 나온다고 한다. 엄밀한 HDR 렌더링 순서로서는 전혀 올바르지 않은 것이지만, 대체로 부자연스러움은 없다고 한다.

톤매핑 된 후의 LDR프레임

여기부터 고휘도부를 뭉개서 Bloom 효과 프레임을 생성


이것들을 합성


이 방식의 메리트는, 블렌딩, MSAA등을 LDR 버퍼에서 할 수 있다는 것. 즉, FP16-64비트 버퍼로 MSAA를 사용할 수 없는 다이렉트X 9 세대의 GPU에서도 호환성이 보증된다는 것이다. 이 타협안적인 리얼 HDR 렌더링은 「Half-Life2:LostCoast」(VALVE,2005), 「Half-Life2:EPISODE ONE」(VALVE,2006)에서 사용되었다.

타협안적 리얼 HDR 렌더링 기법을 구현한 「Half-Life2:LostCoast」(VALVE,2005)로 부터. 왼쪽이2004년의 오리지널 「Half-Life2」시대의 유사 HDR 렌더링. 오른쪽이 타협안적인 리얼 HDR 렌더링에 의한 것. 오른편은 수면에 비친 태양도 제대로 Bloom 효과가 나오는 것을 알 수 있다.


2007년에 발매된 윈도우즈 비스타와 거의 동시에 제공된 다이렉트X 10 세대 / SM4.0 세대의 GPU에서는 FP16-64비트 버퍼, FP32-128비트 버퍼에 대해서도 MSAA 처리를 적용할 수 있게 되어, 이상적인 HDR렌더링의 공정이 모든 메이커의 GPU에서 구현될 수 있게 되었다.

이렇게 보면, 2002년 경 부터 시작된 유사 HDR 렌더링의 기간이 의외로 길고, 최근이 되어서야 드디어 이론대로의  진짜 HDR 렌더링을 구현할 수 있게 되었다는 것을 알 수 있다.(계속)


여기부터는, HDR렌더링은, 구체적으로 어떤 프로세스로 실현되는지를 해설해 나가고 싶다.

처음에는 전체적인 흐름을 보고 나서, 그 요소 요소에서의, 구현에 필요한 각종 기술들을 소개해 나가고 싶다. 덧붙여, 각 포인트에서는, 오랫동안 주류가 되어 온 「유사 HDR 렌더링 기법」과, 앞으로 주류가 될 「리얼 HDR 렌더링 기법」양쪽 모두를 언급하고 싶다.


HDR렌더링의 프로세스

HDR 렌더링은, 렌더링을 HDR 차원에서 수행하는 것이 기본 컨셉이 된다.

지난회 까지에서 종래의 αRGB 각 8비트의 int8-32비트의 정수 버퍼로 유사하게 HDR 렌더링을 하는 유사 HDR 렌더링과, αRGB 각 FP16비트의 FP16-64비트의 부동소수점 버퍼로 실시하는 리얼 HDR 렌더링이 있다는 것은 이야기했다. 또, 후반에는 VALVE가 「Half-Life2:EPISODE TWO」에서 구현한 타협안적인 리얼 HDR 렌더링에 대해서도 소개했다. 지금까지 소개한 여러가지 접근의 HDR 렌더링 스타일은 어쨋든, 이론을 이상적으로 구현한 HDR 렌더링은 그림1과 같은 흐름이 된다.

                                그림1: HDR 렌더링의 이상적인 렌더링 파이프라인

<그림 설명>
(박스 좌) HDRテクスチャ(HDR 텍스처)、HDR高原(HDR 광원)、HDRレンダーターゲット(HDR 렌더타겟)、
(박스) HDRフレームバッファ(HDR 프레임버퍼)、
(버블) 동적인 환경맵 등을 이용
(박스) HDRブルーム/グレア(HDR Bloom/Glare)、
(박스) トーンマッピング (톤매핑)
(박스 우) 表示(표시)
-> αRGB가 각각 16비트인 HDR
-> αRGB가 각각 8비트인 LDR


이 그림을 해설하도록 하자.

좌측 맨아래의, 「HDR 렌더타겟」……즉 HDR렌더링에 이용하는 버퍼는, FP16-64비트 버퍼가 표준이 된다. 앞세대의 다이렉트X9 세대, SM2.0/SM3.0 대응 GPU에서 완벽하지 않았던 FP16-64비트 버퍼의 렌더타겟에 대한 멀티샘플 안티에일리어싱(MSAA) 처리가, 다이렉트X10 세대/SM4.0 대응 GPU에서는 동작이 보증되었기 때문에, 비디오메모리 점유량 증가, 메모리 버스 소비를 제외하면 사용 용이성은 거의 int8-32비트 정수 버퍼와 다르지 않은 레벨로 끌어 올려졌다. HDR 기록을 위해 필요한 다이나믹 레인지도, 리얼타임 3D그래픽스용으로서는 필요 충분하다.

좌측 가운데의, 「HDR광원」……은 HDR 렌더링에 이용하기 위한 광원으로, int8-32비트에 제한 받지 않는 고휘도 광원을 사용할 필요가 생긴다.

좌측 맨위의, 「HDR 텍스처」는, HDR 렌더링 시에 3D모델등에 적용하는 텍스처도 HDR포맷에 대응한다는 것……을 말하고 있다.

3D모델에 붙이는 화상 텍스처(Decal Texture)는, 통상의 int8-32비트의 LDR 텍스처라도 상관없지만, 주위의 비쳐지는 정경을 나타내는 환경 맵이나, 텍스처 자체를 발광물(發光物)로서 다루는 「자체 발광 텍스처」에 대해서는 HDR 텍스처를 사용하는 것이 바람직하다. 구체적으로는 이 경우도 FP16-64비트의 텍스처가 이상적이다.

다만, FP16-64비트의 HDR 텍스처는 그대로는 압축을 할 수 없기 때문에 다루기 어렵다. 또, FP16-64비트의HDR 텍스처를 읽는 횟수는 퍼포먼스에 큰 영향을 주므로, 3D게임등의 경우는 비주얼 품질에 상당히 영향이 크다고 판단되는 텍스처 이외에는, int8-32비트의 LDR 텍스처에 HDR 정보를 담는, 말하자면 유사 HDR 텍스처를 이용하는 경우가 많은 듯 하다. 이 구현 방법에 대해서는 뒤에서 이야기 한다.

이렇게 해서, 「HDR 텍스처」나 「HDR 광원」을 이용해, HDR 렌더링을 수행해서 「HDR 렌더타겟」에 HDR 프레임이 생성된다. 완성된 프레임이 「HDR 프레임 버퍼」이다.

이후, 이 HDR 프레임을 검증해서, HDR프레임 안의 일정 휘도 이상의 고휘도 부분을 추출해서, Bloom이나  Glare 효과 프레임을 생성한다. 이것이 「HDR Bloom/Glare」의 처리계이다.

「HDR 톤매핑」은 HDR 프레임을 일반적인 디스플레이 기기에 표시하기 위해 처리를 하는 곳이다. 간단하게 말하면 감색처리와 같은 것이지만, 앞절에서 말한 것처럼 여기서 그 프레임의 평균 휘도등을 산출해(≒히스토그램을 구한다), 그 프레임에 적절한 휘도 밸런스로 조정하는 것으로, 눈이나 카메라의 자동 노출 보정 효과를 연출할 수 있다.

여기서 부터는, 이 그림1에서 보여진, HDR렌더링 파이프라인의 각 공정들에 대한 포인트들을 해설해 나가기로 하겠다.(계속)


호환성과 퍼포먼스를 중시한다면 유사 HDR 렌더링?

다이렉트X 10 세대 / SM4.0 대응 GPU에서는, FP16-64비트의 렌더타겟을 그냥 사용하면 된다. 폭넓은 다이나믹 레인지를, FP16-64비트라면 특별한 가공처리 없이 절대값으로 렌더링 할 수 있다.

그 이전의 다이렉트X 9 세대 / SM2.0~3.0 대응 GPU에서도, 호환성이나 퍼포먼스의 문제를 해결할 수 있다면, FP16-64비트 버퍼를 사용한다.

그렇지 않은 경우는, int8-32비트의 정수 LDR 버퍼를 이용한 유사 HDR 렌더링를 하는 것이 적합하다고 여겨진다.

8비트 정수이면 0~255의 값을 표현할 수 있지만, 보통, 이것을 0.0~1.0의 값에 대응 시켜 휘도를 표현한다. 이것을 예를 들면, 0~255를 0.0~2.0까지의 값으로 대응 시켜, 보통 때의 2배의 다이나믹 레인지(표현영역)를 가진다고 간주하는 것이다. 즉, 이미지적으로는 색 표현의 분해능(해상도)을 절반으로 하는 대신에, 휘도 표현을 2배로 한다…… 고 간주하고 렌더링을 하는 것이다. 즉, 보통의 LDR 렌더링에서는 최대로 밝은 255가  이 유사 HDR 렌더링 기법에서는128이 되는 것이다.

이대로 유사 HDR 렌더링을 모두 끝내고, 뒷단계인 Bloom/Glare 생성을 끝내고 톤매핑을 실시하면 폭이 127이였던 범위가 0~255의 범위로 되돌려지게 된다.

물론, 0~255을 0.0~4.0까지……와 같이 다이나믹 레인지를 한층 더 넓히는 것도 가능은 하다. 하지만, 뒷단계인 톤매핑 처리로, 폭이 63인 범위가 0~255로 되돌려지게 되어, 분해능(해상도)은 앞에서 이야기한 0.0~2.0의 경우 보다 더욱더 저하된다.

그 때문에, 이 기법을 사용하는 경우에는 「0~255→0.0~2.0」으로 하는 경우가 많은 것 같다.

앞에서 소개했던 유사 HDR 렌더링의 원조적 존재인 「DOUBLE S.T.E.A.L」이나, 「발키리프로파일2」(트라이 에이스,2006)등에서도, 「0~255→0.0~2.0」으로 한 유사 HDR 렌더링을 채택했다.(계속)

왼쪽에서 부터 0.0~1.0(보통), 0.0~2.0(2배의 휘도까지 대응한 유사 HDR), 0.0~4.0(4배의 휘도까지 대응한 유사 HDR). 2배에서는 약간, 4배에서는 심하게 유사 윤곽(마하 밴드)이 눈에 띈다.

위 그림에 화상 텍스처를 적용한 경우. 화상 텍스처를 적용하면 유사 윤곽은 시각적으로, 대부분 눈에 띄지 않게 된다. 특히2배에서는 잘 보지 않으면, 알지 못할 정도이다.
(화상 출처 = 트라이 에이스/Practical Implementation of SH Lighting and HDR Rendering on PlayStation 2/Yoshiharu Gotanda/Tatsuya Shoji/Research and Development Dept. tri-Ace Inc.)

지금도 유사 HDR 텍스처와 병용 하는 것이 주류

HDR 텍스처도, 다이렉트X 9 세대 / SM3.0 대응 이후의 GPU이면, 보통의 부동소수점 텍스처를 활용하면 문제는 없다. FP16-64비트 텍스처이면, 텍스처 필터링도 바이리니어로부터 이방성까지 모두 적용할 수 있다. 다이렉트X 10 세대 / SM4.0 대응 GPU가 아니면 FP32-128비트 텍스처의 필터링은 할 수 없지만, 일반적인 3D그래픽스의 렌더링에서 여기까지의 HDR 텍스처를 필요로 하는 상황은 별로 없을 것이다.

하지만, FP16-64비트 텍스처의 범용성 그 자체는 높지만, 커다란 문제도 있다. 그것은 비디오 메모리의 점유량과 읽어낼 때의 대역 소비가 크다는 것이다(렌더타겟 경우와 똑같은 문제).

αRGB 각 8비트의 int8-32비트의 종래의 LDR 텍스처이면, DXTC(DirectX Texture Compression)으로 불리는  텍스처 압축 방법를 이용할 수 있다. DXTC로 압축된 텍스처는, 압축된 상태인 채로 GPU 코어가 직접 액세스 할 수 있기 때문에, 활용하면 상당히 비디오메모리를 절약할 수 있다. 그러나, 부동소수점의 HDR 텍스처가 되버리면, 최신의 다이렉트X 10 세대 / SM4.0 대응 GPU 라 할지라도, 그 압축 방법은 지원되지 않는다.

3D게임 그래픽스에 있어서, 모든 텍스처를 HDR 텍스처로 하려면 , 비디오메모리 예산과, 대역 예산이 너무 커져버리기 때문에, 다이렉트X 9 세대 / 다이렉트X 10 세대에서도, LDR 텍스처를 유사 HDR 텍스처적으로 활용하는  경우가 많은 것 같다.

유사 HDR 텍스처는, 포맷으로는 LDR 텍스처이므로, DXTC를 이용해 효과적으로 압축할 수 있다.

포인트가 되는 것은, 어떻게 LDR 텍스처에 HDR 정보를 저장할까이지만, 이것에 대해서는 여러가지 테크닉이 고안 되어 있다.

여기에서는 「로스트 플라넷」(캡콤, 2007)에서 사용한 방법을 소개하겠다.

이 방법에서 이용하는 것은 α와 RGB값 모두 1/4로 압축된 DXT5 모드의 DXTC이다.

HDR로부터 LDR로의 변환(encode)은, 우선,{R,G,B,1.0} 중 최대값 M을 선택해, 그 역수 1/M을 α에 저장한다. 이어서 {R,G,B} 모두를 M으로 나눈 것을 RGB에 저장한다.

원래대로 되돌리는 디코딩 처리는 {R,G,B}={R,G,B}÷α만으로 구할 수 있다.

이것으로, 분해능(해상도)은 저하되지만, HDR의 다이나믹 레인지는 보존되고, 그 나름대로 RGB의 칼러도 보존된다.

HDR 텍스처라고 해도, 고휘도인 HDR값인 텍셀 보다는, RGB가 0.0~1.0의 LDR값인 텍셀들이 더 많을 것이다. 이 방법은, 그 계산 알고리즘의 특성상, 대부분을 차지하는 LDR값인 텍셀들의 보존품질이 높아지는 것이 특징이다.

또, 인코딩하면, HDR값으로 돌아오므로 FP16-64비트의 HDR 텍스처와의 혼용도 용이하다고 할 수 있다. 게다가 이 방법은, 고압축율인 DXT1 으로 압축한 LDR 텍스처를 동일 셰이더로 다룰 수 있다는 메리트도 있다.

DXT1은 α=0 아니면 α=1로 밖에는 이용할 수 없지만, α=1로서 LDR 텍스처를 DXT1으로 압축해 두면, 방금전의 DXT5의 유사 HDR 텍스처를 {R,G,B}={R,G,B}÷α로 디코딩했다고 해도 {R,G,B}={R,G,B}÷1 이므로, 앞뒤가 잘 맞는다.(계속)

HDR 텍스처를 유사 HDR 텍스처로서 LDR 텍스처로 변환. 그 나름대로 품질이 괜찮은 HDR 텍스처는, 이것을 이용하면 DXT압축을 할 수 있다.


넘쳐 나오는 빛을 생성하는 방법

Bloom 효과나 Glare 효과는, 렌더링 한 프레임 안에서의 고휘도 부분을 추출해서 실시하게 된다. 말하자면 Paint 소프트웨어에서 photo retouch를 하는 행위를 픽셀셰이더에서 하는 이미지다.

유사 HDR 렌더링으로 하든, 리얼 HDR 렌더링으로 하든, 하이 다이나믹 레인지의 휘도 정보가 각 화소에 기록되어 있으므로, 「어디부터가 고휘도인가?」를 판단하지 않으면 안 된다.

예를 들어 0.0~2.0까지의 휘도값을 저장하고 있다고 하고 폭 1.0의 범위를 표시하는 경우를 생각해 보자. 그 프레임이 1.0~2.0의 분포였을 때, 1.0은 가장 어두운 부분이 되지만, 역으로 0.0~1.0의 분포였을 때 1.0은 가장 밝은 부분이 된다. HDR 렌더링을 한 그 프레임의 평균 휘도를 구해, 어디 부터를 「고휘도이다」라고 판단할 수 있을지를 결정해 줄 필요가 있는 것이다. 이 처리에 대해서는 뒤의 톤매핑 해설에서 자세하게 다루기로 한다.

                                톤 매핑의 필요성

<그림 설명>
상:
0.0~2.0까지의 휘도를 기록하고 있을 때
중:
기록한 휘도값이 1.0~2.0일 때는 1.0이 가장 어둡다.
(버블) 어둡다!
하:
기록한 휘도값이 0.0~1.0일 때는 역으로 1.0이 가장 밝게 된다.
(버블) 밝다!


그 프레임에 대하여 「고휘도」라고 판단할 수 있는 「휘도 기준치」를 얻을 수 있었다면, 그 값 이상의 휘도의 화소를 다른 버퍼에 추출해 준다.

이렇게 해서 "정제(精製)"한 고휘도 추출 프레임을 "원래 화상"으로 하고, Bloom 효과나 Glare 효과의 이펙트 화상을 생성하는 것이, 이 「HDR Bloom/Glare 효과」의 공정이 된다.

Bloom 효과는, 이 고휘도 부분이 지그시 "배어 나오는" 효과이면 되는 것이므로, 이 고휘도 추출 프레임을 뭉개는 처리를 해 주면 된다라는 방침에 생각이 미친다.

여기서, 포인트가 되는 것은 그 뭉개는(Blur) 방법이다.

뭉개 줄 때 사용되는 블러 필터로서 기본적인 것은 「가우스 필터」(Gaussian Filter)이다.

이것은 간단하게 말하면, 어느 픽셀에 시선을 집중했을 때에, 그 픽셀컬러를 밖으로 가면 갈수록 연하게, 옅게, 동심원 모양으로 흩트리는 처리계이다.

넓은 범위를 블러하기 위해서는, 대규모의 블러 처리를 반복해서 실시 할 필요가 있기 때문에 부하가 크다.

거기서, 고휘도 추출 프레임을 다운샘플링 해서 저해상도판의 고휘도 추출 프레임을 생성해, 이것에 대해서 적당한 규모의 가우스 필터를 적용해 주는 대안이 자주 이용된다. 저해상도판의 고휘도 추출 프레임은 해상도가1/2인 것부터 1/4, 1/8, 1/16, 1/32, 1/64과 같이 적당하게 복수개를 준비하는 것이 일반적이다.

Bloom 효과 자체에 그 정도의 고해상도 정보가 필요없는 경우는, 처음에 생성한 고휘도 추출 프레임을 원프레임 해상도의 1/4 단위로 해서, 거기서 부터 시작해 1/8, 1/16, 1/32, 1/64등의 저해상도판을 준비한다……라고 하는 약식 방법도 생각할 수 있다.

또한, 블러를 할 때는, 저해상도판의 고휘도 추출 프레임 만큼 어둡게 블러되도록, Gauss 필터 적용시의 픽셀 컬러의 감쇠율을 낮추어 준다.(Low-pass화)

1/4 x 1/4 (256x192 pixels)

1/8 x 1/8 (128x96 pixels)

1/16 x 1/16 (64x48 pixels)

1/32 x 1/32 (32x48 pixels)

1/64 x 1/64 (16x12 pixels)


단계적으로 해상도화한 고휘도 추출 프레임들에 대해서, 다른 감쇠율의 Gauss 필터를 적용 ※Practical Implementation of High Dynamic Range Rendering/Masaki Kawase / BUNKASHA GAMES/BUNKASHA PUBLISHING CO.,LTD로 부터


이렇게 만들어진, 블러를 해준, 해상도가 서로 완전히 다른 복수의 고휘도 추출 프레임들을, 바이리니어 필터를 적용해서 동일한 해상도로 확대해, 이것들을 합성해 주면, 멀리 가면 갈수록 연하고 큰 블러된 Bloom효과의 프레임을 얻을 수 있다.

「맨처음 고휘도 추출 프레임을 저해상도로 해주고, 합성시에는 원래의 해상도로 확대해서 되돌린다」……라고 하는 테크닉은, 본래라면 반복해서 흩트리지 않으면 실현될 수 없는 듯한 광범위한 블러를, 1회만의 블러 처리로 실현시키기 위한 테크닉이었다는 것을 알 수 있다. 물론 저해상도가 된 것으로 색과 해상도의 정보량은 격감되어 있는 것이지만, 외곽으로 가면 갈수록 색을 연하게 블러하는 것을 원했으므로, 해상도 역시 그리 중요하지는 않게 된다. 실제로, 눈에 보이는 품질도 그리 나쁘지는 않다.

같은 해상도로 확대해서 모두 합성

 

광범위하게 흐려져 나오는 효과 프레임 완성


이 기법은, 앞절에서 소개한 유사 HDR 렌더링의 원조인 「DOUBLE S.T.E.A.L」에서 구현되어 화제를 부른 테크닉으로 「축소 버퍼에의 가우스 필터」법으로 불리고, 현재는 널리 활용되고 있다. 덧붙여 이 테크닉의 고안자인 개발자의 이름을 붙여서 카와세식 MGF(Multiple Gaussian Filter)로 불리기도 한다.

덧붙여 정수 LDR 버퍼를 사용해 유사 HDR 렌더링을 구현한 「DOUBLE S.T.E.A.L」에서는, HDR 환경 맵이나 고휘도 텍스처의 α부를 휘도 정보 저장용으로 이용해, 이 정보를 효과적으로 활용함으로써, Bloom/Glare 효과의 처리를 효율적으로 할 수 있도록 했다.

씬의 렌더링 시에 이러한 HDR환경 맵이나 고휘도 텍스처를 적용하는 상황에서는, 그러한 α부로부터 휘도 정보를 꺼내서, 음영 계산을 이 휘도 정보에 대해서도 실시해, 렌더링 결과의 컬러와 함께 출력되도록 한다. 최종 렌더링 결과의 α에는, 유사 HDR 렌더링을 하면서도, 음영 처리의 결과로서 고휘도가 된 정보가 남기 때문에,  2차 반사 이후의 표현에서도 제대로 HDR 표현을 실현할 수 있다. 그리고, Bloom/Glare 효과의 공정에서는, 이 α정보를 이용하면 좋다.

유사 HDR 렌더링에서의 Bloom/Glare 효과/CEDEC2002 DOUBLE-S.T.E.A.L.에서의 리얼타임 CG 표현 기법 ※분카사 게임 개발 사업부 카와세 마사키로 부터


Glare 효과(광선, 스타 효과)도, 고휘도부의 "넘치게 하는 방법"이 다를 뿐, 원리는 Bloom 효과와 완전히 똑같다. 어떤 넘치게 하는 방법을 구현할까는 아티스틱한 센스에 맡길 수 있는 부분이다.

「스타 대양3」(트라이 에이스,2003)에서는, 고휘도 추출 프레임을 문질러서 회전시켜 블러해서, 이것을 합성시에는 회전을 원래대로 되돌려서 해상도를 맞추는 형태로 확대해서 합성하는 기법으로, 날카로운 광선의 Glare효과의 이펙트를 만들어 냈다.(계속)

「스타 대양3」(트라이 에이스,2003)으로 부터. 이 예에서는 4 방향, 십자상으로 빛이 넘쳐서 나오는 광선의 Glare 표현 생성의 흐름을 보여주고 있다.

<그림 설명>
왼쪽에서 부터:
(박스) 회전해서 압축
Glare효과 생성용 Work Buffer
(박스) 뭉개준다
(박스) 회전을 원래대로 되돌려 확대
프레임버퍼
(박스) 모두 합성

 

    「스타 대양3」(트라이 에이스,2003)에서의 광선


평균휘도를 어떻게 구할 것인가? 휘도변환을 어떻게 해 줄 것인가?

톤매핑은 평균휘도를 어떻게 구할 것인가, 어떤 방법으로 휘도변환을 할 것인가가 포인트가 된다.

지난회까지 해설한 것처럼, HDR렌더링을 해서 완성된 영상 프레임은 그대로는 표시할 수가 없다( 표시해도 의도한 대로 나오지 않는다).

거기서 필요한 것이, 표시에 적합한 휘도 레인지로 변환해 주는 처리, 즉 「톤매핑」(Tone Mapping) 처리이다.

카메라나 인간의 시각이 그렇듯, 올바르게 영상으로 지각할 수 있는 것은, 어느 일정한 휘도 범위에 한정되므로, 그 HDR 프레임의 평균 휘도를 구해, 그곳을 중심으로 일정한 휘도 범위로 변환해, 표시 가능한 영상을 생성한다.

톤매핑의 이미지. 카메라에서 이른바, 히스토그램을 산출해 이것을 바탕으로 노광조정을 실시하는 것과 유사한 이미지(※Practical Implementation of SH Lighting and HDR Rendering on PlayStation 2/Yoshiharu Gotanda/Tatsuya Shoji/Research and Development Dept. tri-Ace Inc.로 부터)

이 3개는 모두 동일한 씬의 영상들이지만, 어느 휘도를 기준으로 영상을 조정하는가에 따라서, 씬의 모습을 바꿀 수 있다. 이것이 「톤 매핑」의 기본적인 개념. (※Recovering High Dynamic Range Radiance Maps from Photographs/Paul Debevec으로 부터)


구한 평균 휘도가 어두우면, HDR 프레임 안의 어두운 색이라 할지라도 그 나름대로 밝게 보이게 할 수 있고, 표시 범위를 크게 넘어버린 휘도의 픽셀은 모두 흰색으로 포화해 버린다.

계산해서 구한 기준이 되는 평균 휘도값을 키(Key)로 해서, HDR 프레임 내의 픽셀 컬러들을 표시 가능한 1,677만색으로 가중치를 붙여 라운딩해 나가는 처리가 「톤매핑」의 처리인 것이다.

가장 단순한 것은, 평균 휘도를 중심으로 선형으로 변환하는 처리이지만, 일반적으로, 이 톤매핑에 이용하는 변환함수의 곡선이, 비선형적인 모양인 것이 실제의 시각에 가까워 더 리얼해 진다고 알려져 있다.

구체적으로는 휘도가 낮은 곳은 약간 들어 올린다는 기분으로 하고, 밝은 곳은 그만큼은 아닌……것 같은 변환 곡선이 좋다고 여겨진다. 비선형의 구현이 어려운 경우에는, 서로 다른 복수의 기울기의 선형 변환을 조합한 멀티 밴드식이 구현되어 지기도 한다.

가장 단순한 선형 변환

   이런 비선형 곡선이 자연스럽게 보인다고
   여겨진다


복수의 선형 함수를 조합해 비선형 곡선을 근사 하는 대안으로도 그 나름대로의 모양을 얻을 수 있다


「발키리 프로파일 2」(트라이 에이스, 2006)으로 부터. 선형 변환의 톤매핑에 의한 영상

3밴드의 선형 변환으로 비선형 커브를 근사 한 톤매핑에 의한 영상


키포인트가 되는 평균 휘도를 구하는 기법에는 몇가지 방법들이 고안되어 있다.

「Half-Life 2: EPISODE ONE」에서는, 연속하는 프레임에 대해서, 각 밴드의 히스토그램을 측정하는 방법으로 평균 휘도를 구하고 있다. 예를 들면 8밴드(8단계)의 히스토그램을 구할 때는, 처음은 완전히 까만색으로부터 1/8의 계조(단계)까지의 휘도를 가진 화소의 개수를 세고, 다음 프레임에서는 다음의 1/8 계조의 휘도를 가진 화소의 개수를 센다. 즉, 8프레임에 걸쳐서 전체 밴드의 히스토그램을 얻는다는 것이다. 물론 실시간 3D그래픽스인 이상, 1프레임 단위로 다른 영상이 되므로, 올바른 히스토그램은 될 수 없겠지만, 연속적인 프레임 간에는 서로 닮아 있으므로 큰 문제는 되지 않는다고 한다.

구체적으로 개수를 센 결과를 어떻게 반영시킬까인데, 그 개수를 세는 방법은 의외로 단순하다. 렌더링 시, 픽셀셰이더로 조사하고 싶은 휘도 레인지의 픽셀이 있으면 MRT를 사용해 대응하는 비표시의 스텐실 버퍼에 마크 한다. 이 스텐실 버퍼에 대해서 비동기의 occlusion query를 실행해서, 마크된 픽셀의 개수를 얻는 것이다.

프레임 단위로 계측하는 대상 휘도를 바꿔서, 복수 프레임에 걸쳐서 계측함으로써 히스토그램을 구하는 「Half-Life 2: EPISODE ONE」(VALVE,2006). 덧붙여 측정 대상은 화면의 중앙 가까이에로 한정한다는 가중치 방법도 사용하고 있다.

「Half-Life 2: EPISODE ONE」(VALVE,2006)에 의한 톤 매핑


평균 휘도의 측정 방법에는 다른 간단한 방법도 있다. 그것은 HDR 프레임을 다운 샘플링 해서 1×1 텍셀까지 축소해서 구하는 방법이다. 이 경우도, 이렇게 구한 평균 휘도 정보는 다음 프레임의 톤 매핑에 활용하는데 문제가 없다.

톤 매핑은 실시간으로 하는 것보다도, 약간의 지연을 수반해 가는 편이, 인간이나 카메라가 밝기에의 순응에 약간 시간이 걸리는 모습을 재현할 수 있어 좀더 리얼해 진다. 또, 복수 프레임에 걸쳐 실행하므로 Fill Rate도 절약 되는 부차적인 혜택도 얻을 수 있다.

축소 버퍼를 이용한 평균 휘도 계측




Posted by 노을삼킨별
,

아래 사이트에서 가져온 정보

Flash 관련 정보를 찾던중 필요한 정보가 있어서 가져왔다.

Flash 관련 카페가 있어서 링크도 걸어본다.

아래는 FD 메인홈
http://www.flashdevelop.org/

어도비 플래시

위키백과 ― 우리 모두의 백과사전.


어도비 플래시(Adobe Flash)는 어도비사의 상호 작용적인 벡터 기반의 웹사이트를 제작할 수 있는 웹 저작 도구이다.

초기 간단한 애니메이션 제작과 상호작용으로 마시마로, 졸라맨 같은 플래시 애니메이션의 대중화를 이끌었으며, 액션 스크립트의 진화 (1.0 > 2.0 > 3.0)와 더불어 RIA (플래시 애니메이션 기술과 서버 응용 기술의 통합을 통한 사용자 중심의 웹페이지 제작), AIR (RIA의 데스크톱 확장) 등의 다양한 응용 프로그램과의 상호 연동을 통하여 그 사용 범위가 확장되고 있다.


버전 [편집]

저작 도구 [편집]

  • 1996년 퓨처 스플래시 애니메이터 (매크로미디어 플래시 1)
  • 1997년 매크로미디어 플래시 2
  • 1998년 매크로미디어 플래시 3
  • 1999년 매크로미디어 플래시 4
  • 2000년 매크로미디어 플래시 5
  • 2002년 매크로미디어 플래시 MX (6)
  • 2003년 매크로미디어 플래시 MX2004 (7)
  • 2003년 매크로미디어 플래시 MX 프로페셔널 2004 (7)
  • 2005년 매크로미디어 플래시 베이직 8
  • 2005년 매크로미디어 플래시 프로페셔널 8
  • 2007년 어도비 플래시 CS3 프로페셔널 (9)
  • 2008년 어도비 플래시 CS4 (10)

플레이어 [편집]

  • 1996년 퓨처 스플래시 플레이어
  • 1997년 매크로미디어 플래시 플레이어 2
  • 1998년 매크로미디어 플래시 플레이어 3
  • 1999년 매크로미디어 플래시 플레이어 4
  • 2000년 매크로미디어 플래시 플레이어 5
  • 2002년 매크로미디어 플래시 플레이어 6
  • 2003년 매크로미디어 플래시 플레이어 7
  • 2005년 매크로미디어 플래시 플레이어 8
  • 2007년 어도비 플래시 플레이어 9
  • 2008년 어도비 플래시 플레이어 10

지원 운영체제/브라우저 [편집]

어도비 플래시 플레이어는 인터넷 익스플로러 5 이상, 파이어폭스 등 엑티브x와 플러그인을 지원하는 브라우저에서 제공된다. 운영체제는 윈도우즈 98이상 작동한다. 95는 작동하지 않는다. 또한 리눅스와 solaris 운영체제에서도 작동한다.

관련 파일 형식과 확장자 [편집]

확장자풀이
.swf.swf 파일은 완전하게 컴파일된, 인터넷 등에 공개된 파일이며 어도비 플래시로 편집할 수 없다. 다만 많은 '.swf 디컴파일러'가 존재하기는 한다. (역자 주: 이러한 프로그램을 사용하여,) 플래시를 사용하는 .swf 파일들을 불러오면 모든 항목은 아니지만 .swf의 일부 항목을 가져올 수 있다.
.fla.fla 파일은 플래시 응용 프로그램을 위한 소스 자료를 포함하고 있다. 플래시 저작 소프트웨어는 FLA 파일을 편집하고 .swf 파일들로 컴파일할 수 있게 도와 준다.
.as.as 파일은 단순한 소스 파일들에 있는 액션 스크립트 소스 코드를 포함한다. FLA 파일들은 또한 액션스크립트 코드를 직접 포함하기도 하지만

각 외부 .as 파일들은 자주 구조적인 이유로 또는 코드를 버전화(versioning) 응용 프로그램에 노출시키기 위해 병합한다. 이들은 .actionscript 확장자를 사용하기도 한다.

.swd.swd 파일은 플래시 개발 도중에 쓰이는 임시 오류 수정 파일이다. 플래시 프로젝트가 개발이 일단 끝나면 이러한 파일들은 필요 없으므로 지워도 된다.
.asc.asc 파일은 서버사이드 액션스크립트를 포함하며, 효과적이고 유동적인 클라이언트 서버 매크로미디어 플래시 커뮤니케이션 서버 MX 응용 프로그램들을 개발하는 데에 쓰인다.
.flv.flv 파일은 어도비 플래시, ffmpeg, Sorenson Squeeze, real player또는 On Flix로 만들어진 플래시 영상 파일이다.
.swc.swc 파일은 구성 요소들을 배포하는 데 쓰인다. 이 파일은 컴파일된 클립, 구성 요소의 액션 스크립트 클래스 파일, 그리고 구성 요소에 대한 설명이 들어있는 다른 파일들을 가지고 있다.
.jsfl.jsfl 파일은 플래시 저작 환경에서 기능을 추가하는 데 쓰인다. 이 파일은 자바스크립트 코드를 포함하며 플래시 자바스크립트 API에 접근할 수 있다.
.swt.swt 파일은 .swf 파일의 템플릿 형식이며 매크로미디어 제네레이터에서 쓰인다.
.flp.flp 파일은 플래시 프로젝트 안에 포함된 모든 문서 파일들을 참조하는 데 쓰이는 XML 파일이다. 플래시 프로젝트는 사용자가 여러 개의 관련된 파일들을 한데 묶을 수 있게 해 주며, 이로써 플래시 프로젝트를 조직하고 컴파일하고 만드는 데 도움을 준다.
.spl.spl 파일은 FutureSplash 문서이다.
.aso.aso 파일은 플래시 개발 도중에 쓰이는 캐시 파일이며, 컴파일된 액션스크립트 바이트 코드를 포함한다. ASO 파일은 이와 일치하는 클래스 파일들의 내용이 바뀌었음을 확인할 때 다시 만들어진다. 가끔 플래시 IDE는 다시 컴파일이 필요하다는 것을 인지하지 못하기 때문에 이러한 캐시 파일들은 사용자가 하나씩 지워 주어야 한다. Win32 / 플래시8에서는 %USERPROFILE%\Local Settings\Application Data\Macromedia\Flash8\en\Configuration\Classes\aso 에 위치해 있다.
.lmv.lmv 파일은 .fla 파일에 같다. 이 파일은 또한 .swf 파일로도 컴파일할 수 있는 오픈 소스 프로그램 Liveswif를 위한 소스 자료이다.

비판 [편집]

이 부분의 본문은 en:Criticism of Adobe Flash입니다.

주로 저사양 컴퓨터에서 CPU 타임(CPU 사용률)을 급격히 높이는 문제를 일으키는 경우가 많다는 지적을 받고 있다.



'게임 개발' 카테고리의 다른 글

은, 는, 이, 가 알아내기 44032  (0) 2010.03.17
Winen - Memory Dump Tool  (0) 2010.01.27
PEDasm 의 신버전 v0.3.3  (0) 2009.07.12
윈도우 프로그래밍 팁  (0) 2008.06.10
chm 도움말 파일 만드는 방법  (0) 2008.04.20
Posted by 노을삼킨별
,

PEDasm 의 사이트를 접속할 기회가 생겨서 봤는데... 신 버전이 업그레이드 되어 있었다.

PEDasm 링크는 여기

다시 보게 될 기회가 생길지는 의문이지만... 일단 올려뒀다. 아래에서 다운 받을 수 있다.

PEDasm v0.3.3 SourceCode Download (139kB)

'게임 개발' 카테고리의 다른 글

Winen - Memory Dump Tool  (0) 2010.01.27
Adobe Flash 관련 정보  (0) 2009.07.18
윈도우 프로그래밍 팁  (0) 2008.06.10
chm 도움말 파일 만드는 방법  (0) 2008.04.20
리눅스 서비스(데몬)의 종류와 관리  (0) 2008.03.18
Posted by 노을삼킨별
,

윈도우 프로그래밍 팁

지정한 디렉토리에 있는 모든 파일을 찾아내는 코드를 만들려면 어떻게 해야 합니까 ?

이 때 사용할 수 있는 API가 바로 FindFirstFile과 FindNextFile, FindClose라는 API들입니다. 사용 예제는 다음과 같습니다.

	WIN32_FIND_DATA  findFileData;
	HANDLE hFileHandle;

	// szDir에 뒤지고자 하는 디렉토리의 경로명을 준다. 예를 들면 "C:\\TEMP\\*.*"
        // 찾아진 파일의 속성은 findFileData의 dwFileAttributes를 살펴본다.
	hFileHandle = FindFirstFile(m_szDir, &findFileData);  
	if (hFileHandle != INVALID_HANDLE_VALUE)   // 파일을 찾은 경우 
	{
		// 찾은 파일의 이름은 cFileName 필드로 들어온다.
		...
		// 다음 파일을 찾는다.
		while(FindNextFile(hFileHandle, &findFileData)) 		{
			...
		}
		FindClose(hFileHandle);
	}     

2. API를 이용하는 유니코드와 ANSI 문자열간의 변환 방법

API를 이용해서 유니코드와 ANSI 문자열간의 변환은 어떻게 수행합니까 ?

Visual C++에서 유니코드 문자열은 BSTR이란 타입으로 표시됩니다. 또 유니코드와 ANSI 문자열간의 변환을 위해서 윈도우 시스템에는 MultiByteToWideChar와 WideCharToMultiByte라는 API가 존재합니다. MFC에서의 BSTR 타입 변환방법이나 ATL로 하는 BSTR 타입 변환도 참고하시기 바랍니다.

  • ANSI 문자열에서 유니코드로의 변환 방법
    	// sTime이란 ANSI 문자열을 bstr이란 이름의 유니코드(BSTR 타입) 변수로 변환
    	char sTime[] = "유니코드 변환 예제";
    	BSTR bstr;
    	// sTime을 유니코드로 변환하기에 앞서 먼저 그 길이를 알아야 한다.
    	int nLen = MultiByteToWideChar(CP_ACP, 0, sTime, lstrlen(sTime), NULL, NULL);
    	// 얻어낸 길이만큼 메모리를 할당한다.
    	bstr = SysAllocStringLen(NULL, nLen);
    	// 이제 변환을 수행한다.
    	MultiByteToWideChar(CP_ACP, 0, sTime, lstrlen(sTime), bstr, nLen);
             // 필요없어지면 제거한다.
             SysFreeString(bstr);
    
  • 유니코드에서 ANSI 문자열로의 변환 방법
    	// newVal이란 BSTR 타입에 있는 유니코드 문자열을 sTime이라는 ANSI 문자열로 변환
    	char *sTime;
             int nLen = WideCharToMultiByte(CP_ACP, 0, newVal, -1, sTime, 0, NULL, NULL);
             sTime = malloc(nLen+1);
    	WideCharToMultiByte(CP_ACP, 0, newVal, -1, sTime, 128, NULL, NULL);
            // 필요없으면 메모리를 제거한다.
            free(sTime);
    
  • 유니코드 문자열을 UTF-8으로 변환하기
         WideCharToMultiByte 함수를 호출할 때 첫 번째 인자로 CP_UTF8을 지정하면 된다. UTF-8은 유니코드의 인코딩 스킴 중의 하나로 쉽게 말하자면 문자열 스트림에서 0을 빼고 표현하는 방법이라고 볼 수 있다.
    

    3. 레지스트리 읽기/쓰기

    API를 이용해서 레지스트리에 한 항목을 생성하거나 기존 항목의 값을 읽어들이려면 어떻게 해야합니까 ?

    레지스트리 관련 API를 사용하려면 winreg.h라는 헤더 파일을 소스에 포함해야 합니다. 레지스트리에 키를 생성하는 방법과 레지스트리에 존재하는 키의 값을 읽는 방법을 차례로 살펴보겠습니다.

  • 레지스트리 키 생성 예제
    	// 예를 들어 HKEY_LOCAL_MACHINE밑의 System\CurrentControlSet\Services\GenPort라는 키를
            // 생성하고 거기에 DWORD 타입의 값으로 Type을 만들고 문자열 타입의 값으로 Group
            // 을 만들어 본다.
    	#include "winreg.h"
    	LONG error = 0;
    	HKEY hKey;
    	DWORD dwDisp, dwData;
    	char lpData[] = "Write this down";
    
    	// 먼저 만들려는 키가 이미 존재하는 것인지 살혀본다.
    	error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\GenPort",
                             0, KEY_ALL_ACCESS, &hKey);
    
    	if (error != ERROR_SUCCESS) // 없다면 새로 생성한다.
    	{
    		// 키를 생성한다.
    		error = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
    			"System\\CurrentControlSet\\Services\\GenPort",	0, "REG_BINARY", 
    		        REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, &dwDisp);
                    // 위의 키 밑에 Type이란 DWORD 타입의 값을 만들고 1로 초기화
    		dwData = 0x1;
    		error = RegSetValueEx( hKey, "Type", 0, REG_DWORD,&dwData,4); 
                    // 위의 키 밑에 Group이란 문자열 타입의 값을 만들고 lpData의 값으로 초기화
    		error = RegSetValueEx( hKey, "Group", 0, REG_SZ, lpData, strlen(lpData)); 
    
                    // 키를 닫는다.
    		RegCloseKey(hKey);	
    	}
    
  • 기존의 레지스트리 키에서 값 읽기
    	// HKEY_CURRENT_USER\Software\Netscape\Netscape Navigator\Main 밑의 Install Directory
            // 값의 문자열 값을 읽어들인다.
    	DWORD dwType, cbData;
    	HKEY hSubKey; 
    	long lRet;
    	char pszString[255];
    
    	// 키를 오픈한다.
    	if ((lRet = RegOpenKeyEx(HKEY_CURRENT_USER, 
    	                "Software\\Netscape\\Netscape Navigator\\Main",
    			0, KEY_READ | KEY_QUERY_VALUE , &hSubKey)) == ERROR_SUCCESS)
    	{
    		cbData = 255;	// 문자열 값을 읽어올 데이터의 크기를 준다.
    		if ((lRet = RegQueryValueEx(hSubKey, "Install Directory",
    			NULL, &dwType, pszString, &cbData)) == ERROR_SUCCESS)
    		{
    			// 제대로 읽힌 경우
    		}
    		else
    		{
    			// 에러가 발생한 경우
    		}
    		RegCloseKey(hSubKey);
    	}
    
  • 레지스트리 키 삭제하기 - RegDeleteKey 함수를 사용한다.

    4. 윈도우 탐색기로부터의 Drag&Drop을 받으려면

    윈도우 탐색기로부터 제가 만든 윈도우로의 drag&drop이 가능하게 하려면 어떻게 해야 합니까 ?

    다음 순서를 따라서 프로그래밍하시면 됩니다.

    1. 프로그램의 초기화시에 DragAcceptFiles(hWnd, TRUE) 함수를 호출한다. 첫 번째 인자인 hWnd는 드롭의 타겟이 되는 윈도우의 핸들이다.
    2. 탐색기로부터 파일이 드롭되는 순간에 WM_DROPFILES 메시지가 날라온다. 이를 처리한다.
      	case WM_DROPFILES :
      	{
      		POINT pt;
      		// 어느 위치에 드롭되었는지 그 항목을 알아낸다.
      		if (DragQueryPoint((HDROP)wParam, &pt)) 
      		{
      			UINT i = 0;
      			// 모두 몇 개의 파일이 드롭되었는지 알아낸다.
      			// 만일 폴더가 드롭되었다면 폴더의 이름만 넘어온다.
      			UINT uCount = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL ,0);
      
      			for(i = 0;i < uCount;i++)
      			{
      				// 드롭된 파일의 이름을 알아온다.
      				DragQueryFile((HDROP)wParam, i, buffer ,255);
      				// 드롭된 파일 이름을 출력해본다.
      				MessageBox(hWnd, buffer, "File Name", MB_OK);
      			}
      		}
      		// drag and drop 작업을 끝낸다.
      		DragFinish((HDROP)wParam);
      		break;
      	}
      
    3. Drag&drop을 더 사용할 필요가 없어지면 DragAcceptFiles를 호출한다.
      	DragAcceptFiles(hWnd, FALSE);
      

    5. 시스템의 모든 드라이브 알아내기

    현재 시스템에 붙어있는 모든 드라이브(네트웍 드라이브 포함)에 대한 정보를 알아내고 싶습니다.

    1. GetLogicalDriveStrings로 시스템에 마운트되어있는 모든 드라이브 정보를 알아낸다. 두 번째 인자인 buffer로 드라이브 정보가 들어오는데 그 구조는 c:\,d:\과 같은 형식이며 리턴값으로 그 버퍼의 크기가 들어온다.
      	char buffer[256];
      	DWORD dwRet;
      	LPSTR token;
      
      	dwRet = GetLogicalDriveStrings(256, buffer);
      
    2. 루프를 돌면서 드라이브별 정보를 알아낸다. 이 때는 GetVolumeInformation 함수를 이용한다.
      	token = buffer; // token이 지금 처리해야할 드라이브를 가리킨다.
      	while (dwRet > 0)
      	{
      		DWORD FileSystemFlag;
      		char FileSystemName[64];
      				
      		strcpy(DriveString, token);
      		// VolumeName으로 드라이브에 대한 설명 문자열이 넘어온다.
      		if (GetVolumeInformation(token, VolumeName, 255, NULL, NULL, 
                                &FileSystemFlag, FileSystemName, 63))
      		{
      	        // 원하는 작업을 수행한다.		
      		}
      		dwRet -= (strlen(token)+1);
      		token = token + strlen(token)+1; // 다음 드라이브로 진행한다.
      	}
      

    6. 드라이브/디렉토리/파일의 이미지 리스트 인덱스 얻기

    특정 드라이브/디렉토리/파일이 시스템 이미지 리스트에서 어떤 인덱스를 갖는지 알고 싶습니다.

    각 파일이나 드라이브 및 디렉토리에 대한 정보는 Shell 라이브러리에서 제공해주는 SHGetFileInfo 함수를 이용하면 됩니다. 다음의 함수는 첫 번째 인자인 lpFileName으로 주어진 파일에 대한 설명을 두 번째 인자로 받아오고 세 번째 인자로는 시스템 이미지 리스트에서의 인덱스를 얻어옵니다.

    	void GetFileInfo(LPSTR lpFileName, LPSTR lpDesc, int *nIndex)
    	{
    	    DWORD dwAttr;
    	    SHFILEINFO sfi;
    
    	    int hIcon = SHGetFileInfo(lpFileName, dwAttr, &sfi, sizeof(SHFILEINFO), 
    				SHGFI_TYPENAME | SHGFI_SYSICONINDEX); 
    	    *nIndex = sfi.iIcon;
    	    strcpy(lpDesc, sfi.szTypeName);
    	}
    

    7. 리스트 컨트롤에 칼럼 헤더 넣기

    리포트뷰 형식의 리스트 컨트롤에 컬럼 헤더를 집어 넣으려면 어떻게 해야합니까 ?

    	// <문서명, 등록날짜, 상태> : 3개의 헤더를 만든다.
    	LV_COLUMN col;
    
    	col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    	col.fmt = LVCFMT_LEFT;
    	col.cx = 100;
    	col.pszText = "문서명";
    	col.cchTextMax = strlen(col.pszText);
    	ListView_SetColumn(hListView, 0, &col); 
    
    	col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    	col.fmt = LVCFMT_LEFT;
    	col.cx = 100;
    	col.pszText = "등록날짜";
    	col.cchTextMax = strlen(col.pszText);
    	ListView_InsertColumn(hListView, 0, &col); 
    
    	col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
    	col.fmt = LVCFMT_LEFT;
    	col.cx = 100;
    	col.pszText = "상태";
    	col.cchTextMax = strlen(col.pszText);
    	ListView_InsertColumn(hListView, 1, &col); 
    

    8. 리스트뷰에 항목 삽입하기

    리스트뷰에 한 항목을 추가하고 싶습니다.

    	// 이미지 리스트와 부가 정보를 사용하지 않는 리스트뷰 컨트롤이다.
    	int nIndex;
    	LV_ITEM item;
    
    	// - 첫번째 컬럼 -
    	item.mask = LVIF_TEXT; // 이미지 리스트를 사용하려면 LVIF_IMAGE를 추가하고
                                   // 부가정보를 지정해야할 일이 있다면 LVIF_PARAM을 추가한다.
    	item.pszText = lpDocName;
    	item.cchTextMax = strlen(lpDocName);
    	item.iItem = 1;
    	item.iSubItem = 0;
    	nIndex = ListView_InsertItem(hListView, &item);	
    	// - 두번째 컬럼 -
    	item.mask = LVIF_TEXT;
    	item.iItem   = nIndex;
    	item.pszText = lpDate;
    	item.cchTextMax = strlen(lpDate);
    	item.iSubItem = 1;
    	ListView_SetItem(hListView, &item); 
    	// - 세번째 컬럼 -
    	item.mask = LVIF_TEXT;
    	item.iItem   = nIndex;
    	item.pszText = "";
    	item.cchTextMax = strlen(lpDocName);
    	item.iSubItem = 2;
    	ListView_SetItem(hListView, &item); 
    

    9. 리스트뷰 컨트롤에서의 정렬 구현

    리스트뷰 컨트롤에서 칼럼 헤더를 눌렀을 때 정렬이 되도록 하려면 어떻게 해야합니까 ?

    1. 일단 리스트뷰 컨트롤의 생성시 윈도우 스타일로 LVS_NOSORTHEADER를 주지 않는다.
    2. 리스트뷰로부터 칼럼 헤더가 눌렸을 때 오는 이벤트를 받아들인다.
      	NM_LISTVIEW *pnmtv = (NM_LISTVIEW FAR *)lParam;
      
      	switch(pnmtv->hdr.code)
      	{
      		case LVN_COLUMNCLICK :
      		{
      			// 어느 항목(pnmtv->iSubItem)이 눌렸는지부터 검사한다. 
      			// g_iSubItem은 어느 항목이 눌렸는지 기록해두는 인덱스이다.
      			g_iSubItem = pnmtv->iSubItem;
      			// 정렬함수를 호출한다. CompareFunc가 정렬함수이다.
      			ListView_SortItems(hListView, (PFNLVCOMPARE)CompareFunc, (LPARAM)this); 
      			break;
      		}
      
    3. 리스트뷰 항목을 정렬하는데 사용되는 CompareFunc라는 함수를 만든다. 이는 보통 C 함수로 만들거나 클래스를 사용할 경우에는 클래스 내의 static 함수로 만든다. CompareFunc의 코드는 다음과 같다.
      	int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
      	{
      		LV_FINDINFO lvfi;
      		int iFirstItem, iSecondItem;
      
      		lvfi.flags = LVFI_PARAM;
      		lvfi.lParam = lParam1;
      		iFirstItem = ListView_FindItem(hListWnd, -1, &lvfi); 
      
      		lvfi.flags = LVFI_PARAM;
      		lvfi.lParam = lParam2;
      		iSecondItem = ListView_FindItem(hListWnd, -1, &lvfi); 
      
      		char lpFirst[100];
      		char lpSecond[100];
      		ListView_GetItemText(hListWnd, iFirstItem, g_iSubItem, lpFisrt, 100);
      		ListView_GetItemText(hListWnd, iSecondItem, g_iSubItem, lpSecond, 100);
      
      		// g_iSubItem 컬럼의 성격에 따라 비교한다. 문자열이라면 아래와 같이 한다.
      		int iRet = strcmpi(lpFirst, lpSecond);	
      		return iRet;
      	}
      

    10. 버전 정보 알아내기 코드

    파일의 버전을 API를 통해 알아내려면 어떻게 해야합니까 ?

    Resource의 한 타입으로 VERSIONINFO라는 것이 존재합니다. 여기에 해당 파일의 버전 정보를 기록하도록 되어있습니다. 이 버전 정보를 읽어오는데 ver.dll이라는 DLL에 들어있는 API들을 사용합니다. 주의할 점은 버전 리소스는 언어별로 설정이 되기 때문에 영어로도 읽어보고 한국어 로도 읽어봐야 한다는 것입니다. 다음 예제를 참고하시기 바랍니다.

    	// szDrvName이란 파일에 들어있는 버전 정보를 읽어온다. 
    	#include 
    
    	DWORD      dwSize, handle;
    	LPSTR      lpstrVffInfo;
    	HANDLE     hMem;
    	LPSTR      lpVersion;	   // 이 변수로 파일의 버전 정보가 들어온다.
    	char       szDrvName[80];  // 버전 정보를 알아내고자 하는 파일 이름이 여기에 들어온다.
    
    	....
    	// 버전 정보 블록의 크기를 알아온다.
    	dwSize = GetFileVersionInfoSize(szDrvName , &handle);
    	if (dwSize) // 버전 정보 블록이 존재하면
    	{ 
    		// 버전 정보 블록을 포함할 메모리 블록을 할당 받아둔다.
    		hMem = GlobalAlloc(GMEM_MOVEABLE, dwSize);
    		lpstrVffInfo  = GlobalLock(hMem);
    		// 버전 정보 블록의 내용을 읽어온다.	
    		GetFileVersionInfo(szDrvName, handle, dwSize, lpstrVffInfo);
    	        // 버전 정보 블록에서 버전 정보를 읽어온다.
            	VerQueryValue((LPVOID)lpstrVffInfo, 
                            (LPSTR)"\\StringFileInfo\\041204B0\\FileVersion",
                            (void FAR* FAR*)&lpVersion, (UINT FAR *)&dwSize);
    		// lpVersion에 들어있는 버전 정보를 사용한다.
    		....
    		GlobalUnlock(hMem);
    		GlobalFree(hMem);
        }
    
    위에서 041204B0가 바로 버전 리소스에 사용된 언어가 무엇인지를 나타냅니다. 이는 영어를 나타내며 한국어의 경우에는 040904B0를 사용하면 됩니다. 이 밖에도 version.lib를 링크의 라이브러리 항목에 추가해야 합니다.

    11. 시스템 사양 알아내기

    현재 시스템에 부착되어 있는 메인 메모리의 양과 CPU와 운영체제의 종류를 알고 싶습니다.

    먼저 시스템에 부착되어 있는 메인 메모리의 크기는 GlobalMemoryStatus라는 API를 이용하면 됩니다. 예제 코드는 다음과 같습니다.

    	//===========================================================
    	// lMemTotal      : 실제 메모리의 전체 크기 (KB 단위)
    	// lAvailMemTotal : 사용 가능한 실제 메모리의 크기 (KB 단위)
    	// lVirtualTotal  : 가상 메모리의 전체 크기  (KB 단위)
    	//===========================================================
    	void GetMemoryStatus(long *lMemTotal, long *lAvailMemTotal, long *lVirtualTotal)
    	{
    		double var;
    		MEMORYSTATUS memoryStatus;
    
    		memset (&memoryStatus, sizeof (MEMORYSTATUS), 0);
    		memoryStatus.dwLength = sizeof (MEMORYSTATUS);
    
    		GlobalMemoryStatus (&memoryStatus);
    
    		lMemTotal = memoryStatus.dwTotalPhys / 1024;
    		lAvailMemTotal = memoryStatus.dwAvailPhys / 1024;
    		lVirtualTotal = memoryStatus.dwTotalVirtual / 1024;
    	}
    
    다음으로 CPU의 종류를 알아내는 코드는 다음과 같습니다.
    	//===============================================================
    	// GetProcessorInfo : 프로세서에 대한 정보를 읽어온다.
    	// lpCPUSpeed      : CPU의 속도. 기록된 시스템에서만 읽어온다.
    	// lpProcessorType : 프로세서의 종류
    	// lpNumProcessors : 프로세서의 개수. NT의 경우에만 의미가 있다.
    	//===============================================================
    	void GetProcessorInfo(LPSTR lpCPUSpeed, LPSTR lpProcessorType, LPSTR lpNumProcessors)
    	{
    		SYSTEM_INFO sysInfo;
    		LONG result;
    		HKEY hKey;
    		DWORD data;
    		DWORD dataSize;
    
    		lpCPUSpeed[0] = 0;
    		// ---------------------------------------------
    		// 프로세서의 속도를 얻어낸다.
    		// ---------------------------------------------
    		result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
    			"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey);
    		if (result == ERROR_SUCCESS) 
    		{
    			result = ::RegQueryValueEx (hKey, "~MHz", NULL, NULL,(LPBYTE)&data, &dataSize);
    			wsprintf(lpCPUSpeed, "%d MHz", data);
    		}
    		RegCloseKey (hKey);
    
    		// ------------------------------------------
    		// 하드웨어 정보를 얻어낸다.
    		// ------------------------------------------
    		GetSystemInfo (&sysInfo);
    
    		// 프로세서 타입부터 검사한다.
    		if (sysInfo.dwProcessorType  == PROCESSOR_INTEL_386)
    			strcpy(lpProcessorType,  "Intel 386");
    		else if (sysInfo.dwProcessorType  == PROCESSOR_INTEL_486)
    			strcpy(lpProcessorType,  "Intel 486");
    		else if (sysInfo.dwProcessorType  == PROCESSOR_INTEL_PENTIUM)
    		{
    			if (sysInfo.wProcessorLevel == 6) 
    				strcpy(lpProcessorType, "Intel Pentium (II/Pro)");
    			else
    				strcpy(lpProcessorType,  "Intel Pentium");
    		}
    		else 
    			strcpy(lpProcessorType, "알 수 없는 시스템");
    
    		// 프로세서의 갯수를 검사한다.
    		wsprintf(lpNumProcessors, "%d", sysInfo.dwNumberOfProcessors);
    	}
    
    
    다음으로 현재 사용 중인 운영체제의 종류를 알아내는 코드는 다음과 같습니다.
    	//===============================================================
    	// GetOSVersion : OS의 버전을 얻어온다.
    	// --------------------------------------------------------------
    	// lpstInfo
    	// lpstBuildNumber
    	// lpstServicePack
    	//===============================================================
    	void GetOSVersion (LPSTR lpstInfo, LPSTR lpstBuildNumber, LPSTR lpstServicePack)
    	{
    		int stat = 0;
    		TCHAR data [64];
    		DWORD dataSize;
    		DWORD win95Info;
    		OSVERSIONINFO versionInfo;
    		HKEY hKey;
    		LONG result;
    
    		lpstServicePack[0] = 0;
    		versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
    
    		// 버전 정보를 얻어낸다.
    		if (!::GetVersionEx (&versionInfo)) 
    		{
    			strcpy(lpstInfo, "운영체제 정보를 얻을 수 없습니다.");
    			return;
    		}
    
    		// NT이면 서버인지 웍스테이션인지 검사한다. 이는 레지스트리를 보고 검사한다.
    		if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) 
    		{
    			strcpy(lpstInfo, "Windows NT");
    			dataSize = sizeof (data);		
    			result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
    				"System\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_QUERY_VALUE, &hKey);
    			if (result != ERROR_SUCCESS) 
    				return;
    
    			result = ::RegQueryValueEx (hKey, "ProductType", NULL, NULL, (LPBYTE) data, &dataSize);
    			RegCloseKey (hKey);
    
    			if (result != ERROR_SUCCESS) 
    				return;
    
    			if (lstrcmpi (data, "WinNT") == 0) 
    				strcpy(lpstInfo, "Windows NT Workstation");
    			else if (lstrcmpi (data, "ServerNT") == 0) 
    				strcpy(lpstInfo, "Windows NT Server");
    			else 
    				strcpy(lpstInfo, "Windows NT Server - Domain Controller");
    
    			// NT 버전을 알아낸다.
    			if (versionInfo.dwMajorVersion == 3 || versionInfo.dwMinorVersion == 51) 
    				strcat(lpstInfo, " 3.51");
    			else if (versionInfo.dwMajorVersion == 5) // 윈도우 2000의 경우
    				strcat(lpstInfo, " 5.0");
    			else 
    				strcat(lpstInfo, " 4.0");
    
    			// Build 번호를 알아낸다.
    			wsprintf(lpstBuildNumber, "%d", versionInfo.dwBuildNumber);
    		}
    		else if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) 
    		{
    			strcpy(lpstInfo, "Windows 95");
    			if ((versionInfo.dwMajorVersion > 4) || ((versionInfo.dwMajorVersion == 4)
    				&& (versionInfo.dwMinorVersion > 0))) 
    			{
    				strcpy(lpstInfo, "Windows 98");
    			}
    			// 윈도우 95는 Build 번호가 하위 워드에 들어간다.
    			win95Info = (DWORD)(LOBYTE(LOWORD(versionInfo.dwBuildNumber)));
    			wsprintf(lpstBuildNumber, "%d", win95Info);
    		}
    		else 
    			wsprintf(lpstInfo, "Windows 3.1");
    
    		// 서비스 팩 정보를 얻어낸다.
    		strcpy(lpstServicePack, versionInfo.szCSDVersion);
    	}
    

    12. IE의 설치 여부와 버전 확인

    현재 시스템에 IE가 설치되었는지 여부와 그 버전을 알려면 어떻게 해야합니까 ?

    사실 동작시켜보지 않고서는 IE가 제대로 설치되어있는지 알아내는 방법은 없지만 레지스트리를 통해 IE가 설치되었는지 여부와 버전을 확인할 수 있습니다. 그 함수는 다음과 같습니다.

    	//===========================================================================
    	// GetIEVersion : IE의 버전을 얻는다. 정보를 찾을 수 없으면 FALSE를 리턴한다.
    	//===========================================================================
    	BOOL GetIEVersion(LPSTR lpVer)
    	{	
    		LONG result;
    		HKEY hKey;
    		DWORD dwType; 
    		char data[65];
    		DWORD dataSize = 64;
    
    		// --------------------
    		// IE의 버전을 얻는다.
    		// --------------------
    		result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Internet Explorer", 0, KEY_QUERY_VALUE, &hKey);
    		if (result == ERROR_SUCCESS) 
    		{
    			result = ::RegQueryValueEx (hKey, "Version", NULL, &dwType, (unsigned char *)data, &dataSize);
    			strcpy(lpVer, data);
    		}
    		else
    			return FALSE;
    
    		RegCloseKey (hKey);
    		return TRUE;
    	}
    

    13. IE의 보안 설정 보기

    IE에 보면 네 단계의 보안 영역이 있습니다. 그 영역별로 설정되어있는 보안 설정값을 읽으려면 어떻게 해야합니까 ?

    IE에는 다음과 같은 네 가지 보안 영역이 존재합니다.

    • 인터넷 영역
    • 로컬 인터넷 영역
    • 신뢰할 수 있는 사이트 영역
    • 제한된 사이트 영역
    IE는 보안 영역 설정과 관련하여 Internet Security Manager와 Internet Zone Manager라는 인터페이스가 존재합니다. 이를 이용해 보안 영역의 보안을 설정하고 특정 IP나 도메인 이름을 등록할 수 있습니다. 자세한 사항은 레퍼런스를 찾아보기 바랍니다.
     
    	#include "objbase.h"
    	#include "urlmon.h"
    
    	char szTemp1[256];
    	char szTemp2[256];
    	HRESULT hr;
    	IInternetSecurityManager *pSecurityMgr;
    	IInternetZoneManager *pZoneMgr;
    	DWORD dwEnum, dwZoneCount;
    	// --- 변수 선언부
    	DWORD dwZone;
    	ZONEATTRIBUTES zoneAttr;
    	int nLevel = 2;
    
    	// COM 라이브러리를 초기화한다.
    	CoInitialize(NULL);
    	dwEnum = 0;
    	pSecurityMgr = NULL;
    	pZoneMgr = NULL;
    
    	// Internet Security 인터페이스 초기화
    	hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_ALL, //INPROC_SERVER,
    			IID_IInternetSecurityManager, (void**)&pSecurityMgr);
    	if (hr != S_OK)
    	{
    		return;
    	}
    	hr = CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_ALL, //INPROC_SERVER,
    			IID_IInternetZoneManager, (void**)&pZoneMgr);	
    	if (hr != S_OK)
    	{
    		return;
    	}
    	dwEnum = 0;
    
    	// 보안 영역 열거자(Zone Enumerator)를 초기화한다.
    	pZoneMgr->CreateZoneEnumerator(&dwEnum, &dwZoneCount, 0);
    	for(DWORD i = 1;i < dwZoneCount;i++)
    	{
    		pZoneMgr->GetZoneAt(dwEnum, i, &dwZone);
    		pZoneMgr->GetZoneAttributes(dwZone, &zoneAttr);
    
    		// zoneAttr.szDisplayName에 보안 영역의 이름이 들어오는데 유니코드이다. 이를 변환한다.
    		WideCharToMultiByte(CP_ACP, 0, zoneAttr.szDisplayName, -1, szTemp1, 255, NULL, NULL);	
    		// zoneAttr.dwTemplateCurrentLevel에는 보안 영역의 보안값 설정이 들어온다.
    		wsprintf(szTemp2, "%x", zoneAttr.dwTemplateCurrentLevel);
    	}
    
    	// 보안 영역 열거자(Zone Enumerator)를 제거한다.
    	if (dwEnum != 0)
    		pZoneMgr->DestroyZoneEnumerator(dwEnum);
    
    	pSecurityMgr->Release();
    	pZoneMgr->Release();
    	// COM 라이브러리를 메모리에서 내린다.
    	CoUninitialize();
    }
    

    14. ActiveX 컨트롤의 등록 방법

    Regsvr32 같은 유틸리티를 이용하지 않고 프로그램 내에서 컨트롤을 레지스트리에 등록하려면 어떻게 해야합니까 ?

    모든 AcitveX 컨트롤은 자신을 레지스트리에 등록하기위한 목적으로 DllRegisterServer라는 함수를 갖고 있습니다. ActiveX 컨트롤을 메모리로 로드한 다음에 이 함수를 불러주면 원하는 일을 수행할 수 있습니다. 반대로 ActiveX 컨트롤을 레지스트리에서 제거하기 위한 용도로 DllUnRegisterServer라는 함 수도 존재합니다.

    	// ==============================================================
    	// RegisterOCX     지정된 ActiveX 컨트롤을 레지스트리에 등록한다.
    	// --------------------------------------------------------------
    	// LPSTR pszString 등록하고자 하는 ActiveX 컨트롤의 절대 경로명
    	// ==============================================================
    	BOOL WINAPI RegisterOCX(LPSTR pszString)
    	{
    		int iReturn = 0;
    		HRESULT (STDAPICALLTYPE * lpDllEntryPoint)();
    		HINSTANCE hLib;
    
    		// OLE 라이브러리를 초기화한다.				
    		if (FAILED(OleInitialize(NULL)))
    		{
    			MessageBox(GetFocus(), "OLE 초기화 실패", "에러", MB_OK);
    			return FALSE;
    		}
    
    		// 지정된 activeX 컨트롤을 메모리로 로드한다.
    		hLib = LoadLibrary(pszString);
    		if (hLib <= NULL)
    		{
    			MessageBox(GetFocus(), "파일을 로드하는데 실패했습니다.", "에러", MB_OK);
    			OleUninitialize();
    			return FALSE;
    		}
    
    		// "DllRegisterServer" 함수의 위치를 찾는다.
    		lpDllEntryPoint = (long (__stdcall *)(void))GetProcAddress(hLib, "DllRegisterServer");
    	
    		// 이 함수를 호출합니다.
    		if (lpDllEntryPoint)
    		{
    			if (FAILED((*lpDllEntryPoint)()))
    			{
    				DWORD dwRet;
    				char szTemp[128];
    
    				dwRet = GetLastError();
    				wsprintf(szTemp, "에러 번호 : %lx", dwRet);
    				MessageBox(GetFocus(), szTemp, "DllRegisterServer 에러", MB_OK);
    				FreeLibrary(hLib);
    				OleUninitialize();
    				return FALSE;
    			}
    		}
    		else
    		{
    			MessageBox(GetFocus(), "DllRegisterServer를 찾을 수 없습니다.", "에러", MB_OK);
    			FreeLibrary(hLib);
    			OleUninitialize();
    			return FALSE;
    		}
    
    		FreeLibrary(hLib);
    		OleUninitialize();
    		return TRUE;
    	}
    

    15. 데이터 파일의 실행

    탐색기에서 실행 파일이 아닌 데이터 파일을 더블클릭하면 그 데이터 파일과 연결된 실행 파일이 실행되면서 그 파일을 물고 올라갑니다. 이를 구현하는 방법을 알려주세요.

    ShellExecuteEx라는 API를 사용하면 됩니다. 다음 함수는 인자로 데이터 파일 혹은 실행 파일의 경로를 받아서 실행해줍니다. 데이터 파일의 경우에는 연결된 실행 파일을 찾아서 그걸 실행해줍니다.

    	BOOL Execute(LPSTR lpPath)
    	{
    		char FilePath[255];
    		SHELLEXECUTEINFO  ExecInfo;
    	
    		// lpPath를 나누어 본다.
    		char drive[_MAX_DRIVE];
    		char dir[_MAX_DIR];
    		char fname[_MAX_FNAME];
    		char ext[_MAX_EXT];
    
    		_splitpath(lpPath, drive, dir, fname, ext);
    		// 디렉토리 경로를 얻는다.
    		strcpy(FilePath, drive);
    		strcat(FilePath, dir);
    
    		// 파일 이름을 얻는다.
    		strcat(fname, ".");
    		strcat(fname, ext);
    
    		SHELLEXECUTEINFO  ExecInfo;
    		ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); 
    		ExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; 
    		ExecInfo.hwnd = hWnd; 
    		ExecInfo.lpVerb = "open"; 
    		ExecInfo.lpFile = fname; 
    		ExecInfo.lpParameters = NULL; 
    		ExecInfo.lpDirectory = FilePath; 
    		ExecInfo.nShow = SW_SHOW; 
    		ExecInfo.hInstApp = g_hInstance; // g_hInstance는 프로그램의 인스턴스 핸들
    
    		return ShellExecuteEx(&ExecInfo);
    	}
    

    16. 파일의 존재 여부 테스트

    어떤 파일이 실제로 존재하는 것인지 간단히 테스트해보는 방법은 무엇인가요 ?

    CreateFile API를 사용하면 됩니다. 파일을 열때 플래그 중의 하나로 OPEN_EXISTING이라는 것이 있는데 이를 사용하면 됩니다. 다음 코드를 예로 보시기 바랍니다.

    	hFile = CreateFile("C:\\TEMP\\test.txt", GENERIC_READ, 0, NULL,  
    			OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    	if (hFile != INVALID_HANDLE_VALUE)
    	{
    		// 파일이 존재하는 경우
    		CloseHandle(hFile);
    	}
    

    17. API를 이용한 파일 I/O

    API를 이용한 파일 입출력에 대한 예제 코드가 없습니까 ?

    프로그래밍을 하다보면 간혹 파일 I/O를 API를 이용해 수행해야 할 경우가 있습니다. 이 때 다음의 예제 코드를 복사해다가 사용하면 편리할 것입니다. MFC의 CFile을 이용한 파일 I/O에 대해 알고 싶으시면 요기를 클릭하세요.

  • 파일 쓰기의 경우
    	HANDLE fp;
    	DWORD NumberOfBytesWritten;
    	char lpBuffer[1024];
    
    	// FileName이 지정한 파일의 이름이 있으면 그걸 열고 없으면 그 이름으로 하나 생성한다.
    	if ((fp=CreateFile((LPCTSTR)FileName, GENERIC_WRITE | GENERIC_READ,	0, NULL, 
    						OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
    	{
    		// 파일 열기 에러 발생
    	}  
    	// 필요한 만큼 WriteFile의 호출을 반복한다. 파일 포인터의 이동시에는 SetFilePointer API를 이용한다.
    	WriteFile(fp, lpBuffer, 1024, &NumberOfBytesWritten, NULL);
    	if (NumberOfBytesWritten != 1024)
    	{
    		// 파일 쓰기 에러 발생 
    		CloseHandle(fp);		
    	}
    	// 작업이 다 끝났으면 파일을 닫는다.
    	CloseHandle(fp);		
    
  • 파일 읽기의 경우
    	HANDLE fp;
    	DWORD NumberOfBytesRead;
    	char lpBuffer[1024];
    
    	if ((fp=CreateFile((LPCTSTR)FileName, GENERIC_READ,	
    		FILE_SHARE_READ, NULL, OPEN_EXISTING, 
    		FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
    	{
    		// 파일 열기 에러 발생
    	}  
    	// 필요한 만큼 ReadFile의 호출을 반복한다.
    	ReadFile(fp, lpBuffer, 1024, &NumberOfBytesRead, NULL);
    	if (NumberOfBytesRead != 1024)
    	{
    		// 파일 읽기 에러 발생 
    		CloseHandle(fp);		
    	}
    	// 작업이 다 끝났으면 파일을 닫는다.
    	CloseHandle(fp);		
    

    파일 포인터의 이동시에는 SetFilePointer라는 API를 사용하고 파일의 크기를 알고 싶을 때는 GetFileSize라는 API를 사용한다.

    18. GetParent API의 리턴값

    다이얼로그 박스에서 GetParent API를 호출했을 때 리턴되는 값이 이상합니다.

    GetParent API의 리턴값은 보통의 윈도우에서는 윈도우 생성시 지정한 부모/자식 윈도우 간의 관계에 따라 달라집니다. 하지만 다이얼로그 박스의 경우에는 다릅니다. 다이얼로그 박스는 GetParent를 호출하면 무조건 그 것이 속한 응용프로그램의 메인 윈도우 핸들이 리턴됩니다.

    19. 특정 프로그램을 실행하고 종료를 기다리기

    특정 프로그램을 실행한 다음에 그 프로그램이 종료될 때 다른 일을 하고 싶습니다. 어떻게 해야합니까 ?

    다음은 CreateProcess를 이용해서 특정 프로그램의 실행이 끝나기를 기다리는 코드입니다.

    	// buffer에 실행하고자하는 파일이름이 들어온다.
    	void DoCreate(HWND hWnd, LPSTR buffer)
    	{
    		STARTUPINFO            sui;
    		PROCESS_INFORMATION    pi;
    		DWORD                  ret;
    
    		memset(&sui, 0x00, sizeof(STARTUPINFO));
    		sui.cb = sizeof(STARTUPINFO);
        
    		ret = CreateProcess(buffer, NULL, NULL, NULL, FALSE, 
    			0, NULL, NULL,&sui, &pi);
    		if (ret == TRUE) // 제대로 실행되었으면
    		{
    			hProcess = pi.hProcess;
    			// 실행이 끝나기를 대기한다.
    			WaitForSingleObject(hProcess, 0xffffffff);
    			CloseHandle(hProcess);
    		}
    	}
    

    20. 파일 열기 다이얼로그 띄우기

    API를 이용해 파일 오픈 다이얼로그를 띄우고 싶습니다.

    API를 이용해 파일 오픈 다이얼로그를 띄우는 방법은 다음과 같습니다.

    	#define MAXCHARS   255
    	OPENFILENAME       ofn;
    	char               buffer[MAXCHARS];
    
    	memset(&ofn, 0x00, sizeof(OPENFILENAME));
    	ofn.lStructSize = sizeof(OPENFILENAME);
    	ofn.hwndOwner = hWnd;
    	ofn.lpstrFilter = "모든 파일\000*.*\000\000";
    	ofn.lpstrInitialDir = m_szTemp;
    	ofn.nFilterIndex = 1;
    	ofn.lpstrFile = buffer;
    	ofn.nMaxFile = MAXCHARS;
    	ofn.lpstrTitle = "파일 선택하기";
    
    	if (GetOpenFileName(&ofn))  // 사용자가 파일을 제대로 선택한 경우
    	{
    		// ....
    	}
    

    21. 긴 파일 이름과 짧은 파일 이름간의 변환 방법

    주어진 긴 파일 이름에 해당하는 짧은 파일 이름을 알려면 어떻게 해야하나요 ?

  • 긴 파일 이름에서 짧은 파일 이름으로의 변환 : GetShortPathName API 사용
  • 짧은 파일 이름에서 긴 파일 이름으로의 변환 : GetFullPathName API 사용

    22. 시스템 이미지 리스트 얻어내기

    탐색기 등에서 사용되는 시스템 이미지 리스트를 사용할 수 있는 방법이 있는지 알고 싶습니다.

    물론 있습니다. SHGetFileInfo라는 API를 이용하면 됩니다. 이를 이용하면 16X16이나 32X32 크기의 이미지 리스트를 얻어낼 수 있습니다.

    	SHFILEINFO sfi;
    	HIMAGELIST sysSmallList, sysLargeList;
    
    	sysSmallList = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi,   sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
    	sysLargeList = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi,   sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_ICON);
    

    23. 리스트뷰 컨트롤에서 스타일 동적 변경하기

    리스트뷰 컨트롤에서 리스트(LVS_ICON) 스타일을 사용 중인데 이를 실행 중에 리포트(LVS_REPORT) 스타일로 변경하고 싶습니다. 어떻게 해야할까요 ?

    윈도우의 스타일은 윈도우 엑스트라 바이트라는 영역에 저장됩니다. 이 곳의 데이터를 읽고 쓰는데 GetWindowLong, SetWindowLong 같은 API를 사용하는데 다음 코드를 예로 보시기 바랍니다.

  • 리스트 스타일에서 리포트 스타일로
    	LONG lStyle = GetWindowLong(hListWnd, GWL_STYLE);
    	SetWindowLong(hListWnd, GWL_STYLE, (lStyle & ~LVS_LIST) | LVS_REPORT);
    
  • 리포트 스타일에서 리스트 스타일로
    	LONG lStyle = GetWindowLong(hListWnd, GWL_STYLE);
    	SetWindowLong(hListWnd, GWL_STYLE, (lStyle & ~LVS_REPORT) | LVS_LIST);
    

    24. 윈도우와 다이얼로그에서 클래스 포인터의 사용

    제가 만든 C++ 클래스내에서 윈도우를 생성합니다. 생성한 윈도우의 윈도우 프로시저는 그 클래스의 정적 멤버 함수로 선언되어 있습니다. 정적 함수의 경우에는 데이터 멤버를 접근하지 못하기 때문에 접근하게 하려고 윈도우를 생성한 그 클래스의 객체에 대한 포인터를 전역 변수로 유지하여 사용하고 있습니다. 별로 깨끗한 방법도 아닌 것 같고 또 동시에 여러 개의 객체가 동작할 경우에는 에러가 날 수밖에 없는데 해결 방법이 없을까요 ?

    이는 다이얼로그의 경우에도 마찬가지입니다. 윈도우 생성시 사용하는 CreateWindow나 CreateWindowEx 같은 함수를 보면 마지막 인자로 윈도우 생성 데이터라는 것을 지정하게 되어있습니다. 다이얼로그 생성시에는 DialogBox라는 API말고 DialogBoxParam이라는 API가 있어서 마지막 인자로 초기화 데이터를 넘겨줄 수 있도록 되어있는데 이것과 윈도우 엑스트라 바이트를 같이 사용하면 정적함수로 선언된 윈도우 프로시저나 다이얼로그박스 프로시저내에서 객체에 대한 포인터를 사용할 수 있습니다. 예를 들어 살펴보겠습니다.

  • 윈도우의 경우

    다음과 같이 CExplorerBar라는 클래스내에서 윈도우를 하나 생성합니다. CreateWindowEx 함수나 CreateWindow 함수의 마지막 인자로 this 포인터를 지정합니다.

    	BOOL CExplorerBar::RegisterAndCreateWindow(void)
    	{
    		....
    		CreateWindowEx( 0,  EB_CLASS_NAME, NULL,
                         WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
                         rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
                         m_hwndParent, NULL, g_hInst, (LPVOID)this);
          
       }
    

    위에서 생성한 윈도우의 윈도우 프로시저는 WndProc이고 CExplorerBar 클래스의 정적 멤버 함수로 존재한다고 가정하겠습니다.

    	LRESULT CALLBACK CExplorerBar::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
    	{
    		CExplorerBar  *pThis = (CExplorerBar*)GetWindowLong(hWnd, GWL_USERDATA);
    
    		switch (uMessage)
    		{
    			case WM_NCCREATE:
    			{
    				LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
    				pThis = (CExplorerBar*)(lpcs->lpCreateParams);
    				SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);
    			}
    			break;
    			case WM_CREATE :
    				return pThis->OnCreate();
    

    WM_NCCREATE 메시지에서 lParam 인자로 넘어오는 CExplorerBar 객체에 대한 포인터를 받아서 윈도우 엑스트라 바이트로 저장하고 있습니다. 윈도우 엑스트라 바이트는 윈도우 마다 할당되는 고유의 영역으로 사용자 정의 영역으로 GWL_USERDATA가 정의되어 있습니다. 여기에다 CExplorerBar 객체에 대한 포인터를 저장해두고 윈도우 프로시저에 진입할 때마다 이 값을 pThis라는 변수에 대입해 놓고 사용합니다. 참고로 WM_NCCREATE는 WM_CREATE 메시지보다 먼저 발생하는 메시지입니다.

  • 다이얼로그의 경우

    예를 들어 CKTree라는 클래스내의 한 멤버 함수에서 다이얼로그 박스를 띄운다고 가정하겠습니다.

    	BOOL CKTree::SelectFolder(short sTypes, long dwFolderID, short  bCreationEnabled)
    	{
    		// ......
    		if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_FOLDER_SELECT), hWnd, (DLGPROC)SelectFolderDlg, (LPARAM)this))
    

    DialogBoxParam 함수는 DialogBox 함수보다 인자가 하나 더 있는데 그것이 바로 다이얼로그 프로시저의 WM_INITDIALOG 메시지의 lParam 인자로 넘어갑니다. 여기에 CKTree 객체에 대한 포인터를 넘깁니다. 그리고나서 다이얼로그 박스 프로시저의 WM_INITDIALOG 메시지에서 이를 받아서

    	LRESULT CALLBACK SelectFolderDlg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    	{
    		switch (message) 
    		{	
    	        case WM_INITDIALOG:
    			{
    				// lParam으로 넘어온 값을 KPointer라는 윈도우 프로퍼티에 저장한다.
    				SetProp(hDlg, "KPointer", (HANDLE)lParam);
    				.......
    			case  WM_NOTIFY :
    			{
    				// CKTree 객체에 대한 포인터가 필요하면 KPointer 윈도우 프로퍼티 값을 읽어들인다.
    				CKTree *pKTree = (CKMartTree *)GetProp(hDlg, "KPointer");
    				if (pKTree->m_bWait)
    				{
    					.....
    

    여기서는 앞서 윈도우와는 달리 윈도우 프로퍼티라는 것을 이용해서 넘어온 포인터 정보를 저장하고 필요할 때 읽어옵니다. 여기서 앞서의 윈도우 엑스트라 바이트를 사용해도 무방합니다.

    25. 특정 프린터로 출력하기

    제 PC에는 두 대의 프린터가 붙어있습니다. 다이얼로그를 띄우지 않고 상황에 따라 다른 프린터로 출력하고 싶은데 동적으로 HDC를 생성하는 방법을 모르겠습니다.

    CreateDC를 이용하면 됩니다. 시스템 디렉토리의 WIN.INI를 보면 [Devices]라는 섹션이 있는데 이 아래로 이 시스템에 설치되어 있는 모든 프린터 드라이버의 목록이 나옵니다. 예를 들어 다음과 같이 나옵니다.

    	[Devices]
    	HUNFAX=HUNFAX,FaxModem
    	삼성 SLB-6216H PCL5=SSMPCL5,\\영업팀\볼륨프린터
    	......
    

    프린터별로 DeviceName=DriverName,OutputName와 같은 구조로 구성되어 있습니다. 이 값들을 CreateDC의 인자로 사용하면 됩니다. 예를 들어 위에서 두 번째 프린터의 DC를 생성하려면 다음과 같이 CreateDC를 호출합니다.

    	// hDC = CreateDC(DriverName, DeviceName, OutputName, NULL);
    	hDC = CreateDC ("SSMPCL5", "삼성 SLB-6216H PCL5", "\\\\영업팀\\볼륨프린터", NULL) ;
    

    CreateDC의 마지막 인자로는 프린터의 설정값을 변경할 수 있습니다. 예를 들어 가로로 찍는다든지 2장을 찍는다든지 하는 설정을 변경하는데 사용됩니다. NULL을 주면 디폴트 값을 사용합니다. 다른 설정을 사용하고 싶다면 다음과 같이 합니다. CreateDC 호출 앞에서 DocumentProperties라는 함수를 호출하여 프린터의 기본 설정을 읽어온 다음에 이를 변경합니다. 다음 예는 출력 방향을 가로로 변경하는 예제입니다.

    	// 프린터의 디폴트 설정을 읽어온다.
    	LPDEVMODE lpoutDevMode;
    	HANDLE hPrinter;
    	HDC hPrnDC;
    
    	// 프린터의 핸들을 얻는다.
    	if (OpenPrinter( lpDeviceName, &hPrinter,  NULL))
    	{
    		// OpenPrinter로 얻은 프린터의 초기 설정을 DocumentProperties API로 얻어온다.
    		// 먼저 마지막 인자를 0으로 해서 DocumentProperties를 호출하여 필요한 버퍼의 크기를 알아옵니다.
    		long lSize = DocumentProperties(GetFocus(), hPrinter, lpPrinterName, NULL, NULL, 0);
    		lpoutDevMode = (LPDEVMODE)malloc(lSize);
    		long lRet = DocumentProperties(GetFocus(), hPrinter, lpPrinterName, lpoutDevMode, NULL, DM_OUT_BUFFER);
    		if (lRet == IDOK)
    		{
    			// 프린터의 인쇄 방향 설정을 변경한다.
    			// 여기서 원하는 변환을 수행한다.
    			lpoutDevMode->dmOrientation = DMORIENT_LANDSCAPE;
    		}
    		hPrnDC = CreateDC (lpDriverName, lpDeviceName, lpOutputName, lpoutDevMode) ;
    		free(lpoutDevMode);
    		return hPrnDC;
    	}
    

    26. 메뉴 관련 함수

    메뉴 항목을 하나 추가하려고 합니다. InsertMenuItem API를 사용하는데 윈도우 3.1에서와 사용법이 다른 것 같습니다.

    사용법이 달라졌습니다.

    	MENUITEMINFO mii;
    
    	memset(&mii, 0x00, sizeof(MENUITEMINFO));
    	mii.cbSize = sizeof(MENUITEMINFO);
    	mii.fMask = MIIM_TYPE;
    	mii.fType = MFT_SEPARATOR;
    	InsertMenuItem(hSubMenu, GetMenuItemCount(hSubMenu), TRUE,  &mii);
    							
    	mii.cbSize = sizeof(MENUITEMINFO);
    	mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
    	mii.fType = MFT_STRING;
    	mii.fState = MFS_DEFAULT | MFS_UNHILITE;
    	mii.wID = ID_WORKPLACE_REMOVE;
    	mii.dwTypeData = "바구니에서 제거";
    	InsertMenuItem(hSubMenu, GetMenuItemCount(hSubMenu), TRUE,  &mii);
    

    27. 코드 실행 중에 다른 윈도우 메시지 처리하기

    하나의 함수 내에서 시간이 오래 걸리는 작업을 하고 있습니다. 이 때 작업 취소를 위한 별도의 다이얼로그를 하나 띄워 두었는데 이 쪽의 버튼이 눌리지 않습니다. 어떻게 해야할까요 ?

    시간이 오래 걸리는 작업을 별도의 스레드로 만들어 처리하던지 아니면 시간이 오래 걸리는 작업을 수행하는 함수 안에서 다음 코드를 가끔 호출해주면 됩니다. 만일 루프를 돌고 있다면 루프내에서 한번씩 호출해주면 됩니다.

    	MSG       msg;
    
    	while (PeekMessage(&msg, NULL, NULL, NULL, TRUE))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    

    VB에서라면 DoEvents라는 메소드를 호출해주면 됩니다.

    28. 메인 윈도우에서 캡션을 제거하고 싶습니다. 어떻게 해야 합니까 ?

    윈도우를 생성할 때 WS_CAPTION이란 스타일을 지정하지 않아도 항상 윈도우의 캡션이 보입니다. 이를 제거하려면 어떻게 해야 하나요 ?

    캡션을 제거하려는 윈도우의 WM_NCCREATE 메시지를 처리해야 합니다. 이 메시지는 WM_CREATE 메시지보다 앞서 발생하는 메시지입니다. NC는 Non-Client를 나타냅니다. GetWindowLong과 SetWindowLong API를 이용해서 WS_CAPTION 스타일을 제거합니다. 이 두 API는 앞서 리스트뷰 컨트롤에서 스타일 동적 변경하기에서 이미 사용해본 바 있습니다.

    		case WM_NCCREATE :
    		{
    			long lStyle;
        
    			lStyle = GetWindowLong(hWnd, GWL_STYLE);
    			lStyle = (lStyle & (~WS_CAPTION)); 
    			SetWindowLong (hWnd, GWL_STYLE, lStyle);
    			return TRUE;
    		}
    

    29. 사각형 형태 이외의 모양을 갖는 윈도우를 띄우고 싶습니다.

    윈도우는 기본 모양이 사각형인데 다른 형태의 모양을 갖는 윈도우를 띄우려면 어떻게 해야합니까 ?

    원하는 모양을 Region이란 것으로 만들어야 합니다. 만든 다음에 이것을 원하는 시점에 SetWindowRgn라는 API를 이용해 윈도우에 설정해주면 됩니다. 예를 들어 타원 모양의 윈도우를 띄우고 싶다면 다음과 같이 해주면 됩니다.

    	HRGN g_hRgn;
    
    	g_hRgn = CreateEllipticRgn(0, 0, 700, 600);
    	SetWindowRgn(hWnd, g_hRgn, FALSE);		
    

    이렇게 했을 경우 윈도우의 위치 이동이 문제가 됩니다. 보통 캡션을 잡고 이동시켜야 하는데 캡션이 없으니까 문제가 됩니다. 이에 관한 것은 31. 윈도우의 이동 처리하기를 참고하기 바랍니다.

    30. 시스템에 설치되어 있는 모든 프린터 드라이버 알아내기

    현재 시스템에 설치되어 있는 모든 프린터 드라이버의 종류를 알아내고 싶습니다.

    EnumPrinters라는 API를 사용하면 됩니다. 다음 코드는 현재 시스템에 설치되어 있는 모든 프린터 드라이버(로컬과 네트웍 프린터 포함)의 이름을 메시지박스로 보여주는 예제입니다.

    	BOOL bSuccess;
    	DWORD cbRequired, cbBuffer, nEntries;
    	PRINTER_INFO_1 *lpBuffer = NULL;
    
    	// 버퍼의 크기를 알아낸다. cbRequired로 들어온다.
    	EnumPrinters(PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL, NULL, 1, (unsigned char *)lpBuffer, 0, &cbRequired, &nEntries);
    	cbBuffer = cbRequired;
    	// 버퍼를 다시 버퍼를 잡는다.
    	lpBuffer = (PRINTER_INFO_1 *)malloc(cbBuffer);
    	// 프린터의 종류를 알아낸다.
    	bSuccess =	EnumPrinters(PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL, NULL, 1, (unsigned char *)lpBuffer, cbRequired, &cbRequired, &nEntries);
    	if (bSuccess == FALSE)
    	{
    		free(lpBuffer);
    		// 다른 이유로 에러가 난 경우
    		return;
    	}
    	// 알아낸 프린터를 하나씩 enumerate한다.
    	for (int i = 0;i < nEntries; i++)
    	{
    		::MessageBox(NULL, lpBuffer[i].pName, "프린터 이름", MB_OK);
      	}
    	free(lpBuffer);
    

    31. 윈도우의 이동 처리하기

    보통 윈도우는 캡션 영역을 잡고 위치 이동을 수행하게 되는데 윈도우의 특정 영역을 잡고 이동할 수 있게 하려면 어떻게 해야합니까 ?

    WM_NCHITTEST라는 메시지를 잘(?) 처리하면 어느 영역이든 윈도우 이동을 처리할 수 있습니다. 마우스로 윈도우 위를 이동하면 WM_NCHITTEST, WM_SETCURSOR, WM_MOUSEMOVE 같은 메시지들이 발생합니다. WM_NCHITTEST는 현재 마우스가 윈도우의 어느 영역위에 있는지 알아내기 위해 사용됩니다. 이 메시지의 처리부에서 HTCAPTION이란 값을 리턴해주면 윈도우 운영체제는 지금 마우스 포인터가 윈도우의 캡션 부분에 와있다고 생각해서 여기서 드래그 작업이 시작될 경우에 윈도우의 위치를 이동시켜 버립니다. 다음 코드는 WM_NCHITTEST 메시지를 처리하여 현재 마우스 좌표가 정해진 영역에 있으면 HTCAPTION을 돌려주는 예제입니다.

    	case WM_NCHITTEST:
    	{
    		// 현재 마우스 위치를 바탕으로 pt 변수를 채운다.
    		POINT pt(LOWORD(lParam), HIWORD(lParam));
    
    		// 마우스 좌표를 윈도우의 좌측 상단 기준의 좌표로 변경한다.
    		ScreenToClient(hWnd, &pt);
    		// 지정된 사각형 안에 포함되는 점인지 검사한다. 
    		// g_TitleRect는 RECT 타입의 변수로 지정된 사각형의 좌표가 들어있다.
    		if (PtInRect(&g_TitleRect, pt))
    			return HTCAPTION;			
    		break;
    	}
    

    32. 바탕 화면 위의 모든 윈도우를 최소화하거나 모든 최소화 실행 취소

    바탕 화면 위의 모든 윈도우를 최소화하거나 모두 최소화 실행 취소를 프로그램으로 구현하는 방법을 알고 싶습니다.

    태스크바의 빈 공간을 오른쪽 마우스 버튼으로 클릭해보면 팝업 메뉴가 뜨는데 거기에 보면 "모든 창을 최소화(M)"와 "모두 최소화 실행 취소(U)" 명령이 존재하는데 그것을 대신 선택해주는 형식으로 프로그램을 작성해주면 됩니다.

    다음은 "모든 창을 최소화"해주는 루틴입니다. keybd_event API를 이용해서 사용자가 키입력한 것처럼 흉내내줍니다.

    	void IconizeAllWindow()
    	{ 
    		keybd_event(0x5b, 0, 0, 0);
    		keybd_event(77, 0, 0, 0);    // 'M' key
    		keybd_event(0x5b, 0, 2, 0);
    	}
    

    다음은 "모두 최소화 실행 취소" 루틴입니다.

    	void RestoreWindowState()
    	{
    		keybd_event(0x5b, 0, 0, 0);
    		keybd_event(84, 0, 0, 0);    // 'U' key
    		keybd_event(0x5b, 0, 2, 0);
    	}
    

    33. VxD 드라이버 호출하기

    Vxd 드라이버를 동적으로 로드해서 호출하고 싶습니다. 어떻게 해야합니까 ?

    먼저 해당하는 VxD 드라이버의 이름과 위치와 호출하려는 작업의 작업 코드명을 알아야 합니다. 드라이버의 로드는 CreateFile API를 이용합니다. VxD 드라이버의 호출은 DeviceIoControl API를 이용합니다. 자세한 설명은 DeviceIoControl API의 레퍼런스를 참고하기 바랍니다.

    	DWORD byteReturned;
    
    	// 먼저 VxD 드라이버를 오픈한다.
    	hDevice = CreateFile("\\\\.\\NMOUSE.VXD", 0, 0, 0, CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE, 0);
    	if (DeviceIoControl(hDevice, W32_SETHWND, &hWnd, 4, NULL, 0, &byteReturned, NULL))
    	{
    		// Success !!!
    	}
    

    Vxd 드라이버는 kernel 모드(이를 윈도우에서는 Ring 0라고 부릅니다)에서 동작하기 때문에 모든 하드웨어와 메모리를 바로 접근할 수 있습니다. VxD 드라이버를 작성하려면 DDK를 이용하거나 Numega의 DriverStudio나 KRFTech사의 WinDriver를 이용해야 합니다.

    34. Thread 실행시 에러

    CreateThread를 이용해 스레드를 만들어 생성하고 있습니다. 루틴에 이상은 없는 것 같은데 스레드가 많이 생성되어 시간이 좀 지나면 에러가 발생합니다. 이유가 무엇일까요 ?

    정말로 스레드 코드에 별 이상이 없다면 CreateThread API 대신에 beginthread나 beginthreadex를 사용해보기 바랍니다. 자세한 사항은 마이크로소프트의 Knowledge base를 참고하시기 바랍니다.

    35. 윈도우 운영체제 종료하기

    프로그램에서 특정 상황이 되면 윈도우 운영체제를 종료하고 싶습니다.

    ExitWindowsEx API를 사용하면 됩니다. 이 함수의 원형은 다음과 같습니다.

    	BOOL ExitWindowsEx(UINT uFlags, DWORD dwReserved);
    

    uFlags로 종료방법을 지정할 수 있습니다. 다음과 같은 값이 가능합니다.

    EWX_LOGOFF 현재 사용자를 로그오프한다.
    EWX_POWEROFF 시스템을 종료하고 파워오프한다. 파워오프는 이를 지원하는 하드웨어에서만 가능하다.
    EWX_REBOOT 시스템을 종료하고 시스템을 재시동한다.
    EWX_SHUTDOWN 시스템을 종료한다.
    EWX_FORCE WM_QUERYSESSION이나 WM_ENDQUERYSESSION을 보내지 않고 실행중인 모든 프로세스를 종료한다. 위의 네 가지 플래그들과 함께 사용할 수 있다.

    36. 디폴트 웹 브라우저 알아내기

    디폴트로 지정된 웹 브라우저를 실행하는 방법을 알고 싶습니다.

    디폴트로 지정된 웹 브라우저는 레지스트리에 자신을 등록합니다. .htm (혹은 .html) 파일의 편집기로 링크도 되지만 http, ftp, gopher 등의 프로토콜 연결 프로그램으로 등록됩니다. 제 생각에 가장 좋은 것은 http 프로토콜의 연결 프로그램을 찾아보는 것으로 생각됩니다. 다음 레지스트리 항목에 보면 연결된 웹 브라우저의 절대 경로를 알 수 있습니다.

        HKEY_CLASSES_ROOT\http\shell\open\command
    

    이 항목의 값을 읽는 방법은 3. 레지스트리 읽기/쓰기를 참고하고 프로그램의 실행에 관한 부분은 19. 특정 프로그램을 실행하고 종료를 기다리기를 참고하거나 ShellExecute API 혹은 WinExec API를 사용하면 됩니다. 이 기능을 수행하는 함수는 다음과 같습니다.

    void LaunchDefaultWebBrowser(HWND hWnd)
    {
        // HKEY_CLASSES_ROOT\http\shell\open\command
    	DWORD dwType, cbData;
    	HKEY hSubKey; 
    	long lRet;
    	LPSTR pszString, pszSrcPath;
    
    	// 키를 오픈한다.
    	if ((lRet = RegOpenKeyEx(HKEY_CLASSES_ROOT, "http\\shell\\open\\command",
    			0, KEY_READ | KEY_QUERY_VALUE , &hSubKey)) == ERROR_SUCCESS)
    	{
    		cbData = 255;	// 문자열 값을 읽어올 데이터의 크기를 준다.
            pszString = (LPSTR)malloc(255);
            pszSrcPath = pszString;
    		if ((lRet = RegQueryValueEx(hSubKey, "",
    			NULL, &dwType, (unsigned char *)pszString, &cbData)) == ERROR_SUCCESS)
    		{
    			// pszString에 디폴트 웹 브라우저의 경로가 들어온다.
                // pszString에서 "를 제거한다.
                RemoveChar(pszString, '"');
                WinExec(pszString, SW_SHOWNORMAL);            
    		}
            free(pszString);
    		RegCloseKey(hSubKey);
    	}
    }
    
    void RemoveChar(LPSTR lpSrc, char chRemove)
    {
    	LPTSTR pstrSource = lpSrc;
    	LPTSTR pstrDest = lpSrc;
    	LPTSTR pstrEnd = lpSrc + strlen(lpSrc);
    
    	while (pstrSource < pstrEnd)
    	{
    		if (*pstrSource != chRemove)
    		{
    			*pstrDest = *pstrSource;
    			pstrDest = _tcsinc(pstrDest);
    		}
    		pstrSource = _tcsinc(pstrSource);
    	}
    	*pstrDest = '\0';
    }
    

    Copyright 1999© 한기용 Last updated: 08/28/2006 18:59:28 Designed By 한기남

    37. 윈도우의 최대/최소 크기 설정

    윈도우의 캡션을 없앴을 경우 윈도우를 최대화했을 때 아래의 Task Bar가 가려져 버리는 현상이 생기는데.. 캡션이 있으면 Task Bar 위로만 최대화되는데 말입니다. 어떻게 해결할 수 있는 방법이 없나 궁금하네요..

    말씀하신 문제를 해결하려면 WM_GETMINMAXINFO 메시지를 처리해야 합니다. 이는 윈도우의 최대 크기 등을 설정하기 위해 사용되는 메시지입니다. 다음과 같이 처리합니다.

    case WM_GETMINMAXINFO :
    {
        LPMINMAXINFO lpmmi;
        RECT rc;
    
        SystemParametersInfo(SPI_GETWORKAREA, 0, &rc,0);
        lpmmi = (LPMINMAXINFO)lParam; 
        lpmmi->ptMaxSize.x = rc.right; 
        lpmmi->ptMaxSize.y = rc.bottom; 
        lpmmi->ptMaxPosition.x = 0; 
        lpmmi->ptMaxPosition.y = 0; 
        lpmmi->ptMinTrackSize.x = GetSystemMetrics(SM_CXMINTRACK); 
        lpmmi->ptMinTrackSize.y = GetSystemMetrics(SM_CYMINTRACK); 
        lpmmi->ptMaxTrackSize.x = GetSystemMetrics(SM_CXMAXTRACK); 
        lpmmi->ptMaxTrackSize.y = GetSystemMetrics(SM_CYMAXTRACK); 
        break;
    }
    

    38. Thread에서 Automation 메소드 호출시 에러 발생

    Thread를 생성하고 Automation 메소드를 호출했는데 에러가 발생합니다.

    App 클래스의 InitInstance 함수에서 AfxOleInit를 호출하는 부분을 CoInitializeEx(NULL, COINIT_MULTITHREADED)를 호출하는 것으로 변경하기 바랍니다. 그리고 App 클래스에 ExitInstance 함수를 추가하고 거기서 CoUninitialize를 호출하도록 하면 됩니다. MFC의 AfxOleInit는 기본적으로 STA(Single Threading Apartment) 모델을 사용합니다. Thread에서 자신이 생성하지 않는 COM 객체를 접근할 때는 MTA(Multiple Threading Apartment) 모델을 사용해야 합니다.

    39. 최상위 윈도우의 종료 방법

    현재 최상위 윈도우를 찾아서 종료하는 코드를 만들고 싶습니다.

    일단 현재 사용자가 작업 중인 최상위 윈도우의 핸들은 GetForegroundWindow API로 얻어냅니다. 그런데 그 윈도우가 자식 윈도우일 수 있기 때문에 GetParent API를 반복적으로 사용해서 최상위 탑 레벨 윈도우의 핸들을 알아냅니다. 종료하는 방법은 먼저 DestroyWindow를 호출해서 시도해보고 실패하면 시스템 메뉴의 "닫기" 명령을 이용해 처리합니다. 사실 이 것도 실패할 수 있는데 무조건 종료시키고 싶다면 아래 코드에서 주석 처리해 놓은 GetWindowThreadProcessId/Terminate API 부분을 사용하면 됩니다.

        HWND hTopWnd = GetForegroundWindow();
        if (hTopWnd == NULL)
        {
            return;
        }
    
        while(GetParent(hTopWnd))
        {
            hTopWnd = GetParent(hTopWnd);
        }
    
        if (DestroyWindow(hTopWnd) == FALSE)
        {
            SendMessage(hTopWnd, WM_SYSCOMMAND, SC_CLOSE, NULL);
            //GetWindowThreadProcessId(hTopWnd, &dwProcessId);
            //TerminateProcess(dwProcessId);
        }            
    
    

    40. 인터넷 익스플로러의 위치 경로 알아내기

    인터넷 익스플로러가 설치된 절대 경로를 알고 싶습니다.

    레지스트리의 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\IExplore.exe 의 기본 값으로 인터넷 익스플로러의 설치 경로가 들어옵니다. 다음 함수를 호출하면 설치 경로를 얻어 줍니다. 인자로 넘어가는 lpPath는 적어도 256바이트 이상의 크기를 갖는 문자 배열이어야 합니다.

    BOOL GetIEPath(LPTSTR lpPath) 
    {
        long lRet;
        HKEY hKey;
    
        lRet = RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\IExplore.exe", &hKey);
        if (lRet == ERROR_SUCCESS)
        {
            long cbData = 255;
    
            RegQueryValue(hKey, "", lpPath, &cbData);
            RegCloseKey(hKey);
        }
        else
            return FALSE;
        return TRUE;
    }
    

  • '게임 개발' 카테고리의 다른 글

    Adobe Flash 관련 정보  (0) 2009.07.18
    PEDasm 의 신버전 v0.3.3  (0) 2009.07.12
    chm 도움말 파일 만드는 방법  (0) 2008.04.20
    리눅스 서비스(데몬)의 종류와 관리  (0) 2008.03.18
    Windows Version List  (0) 2008.01.28
    Posted by 노을삼킨별
    ,