XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   
My XNA Book
      
       Go to section on this site

Additional info


Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


Ads

BMP파일로 만들어진 height map 가져오기

Reimer씨가 height map을 소개하고 가장 많이 받을 질문은 RAW파일 대신 일반적으로 쓰이는 BMP파일을 height map 이미지로 사용할 수 없는가 하는 것 입니다. 아무래도 BMP파일은 RAW파일에 비해 Windows 그림판이나 이미지 편집 프로그램들로 쉽게 다룰 수 있으니까요. 일단은, Reimer씨가 BMP파일 대신 RAW파일을 사용한 이유를 아는 것부터 시작해 봅시다.

RAW파일은 순수한(plain) data의 집합입니다. data이외에 어떠한 정보도 포함하지 않는 단순한 구조이기 때문에 파일내의 byte전부가 data로만 구성되어 있습니다. 따라서 header등을 전처리 해줄 필요 없이 처음부터 끝까지 byte단위로 읽어오면 되므로 매우 간편합니다.

BMP파일은 애초에 이미지용으로 만들어진 파일 포맷입니다. 따라서 이미지 처리에 필요한 많은 부가적인 정보를 담고 있습니다. 포함되는 정보에는 파일 크기, 가로 세로 크기 등이 있습니다. 이렇게 부가적으로 포함되어 있는 정보들은 BMP파일로부터 필요한 data만을 가져오기를 RAW보다 약간 어렵게 합니다. 우리가 할 일은 BMP파일에서 필요 없는 data는 내버려두고 필요한 data만을 해당 영역에서 가져오는 것입니다. 이를 위해 간단하게 BMP파일 포맷을 설명하게 될 텐데, BMP파일 포맷에 관심이 없다면 이 부분은 뛰어넘어도 상관 없습니다.

BMP파일은 header가 가용(actual) pixel data 앞에 위치합니다. 물론 header도 byte data로 구성되어 있습니다. 예를 들어 BMP파일을 메모장으로 열어보면 가장 처음 두 문자는 반드시 ‘BM’입니다. BMP파일을 hex editor에서 열어보면 가장 처음 두 byte는 ‘BM’의 hex값인 66 77입니다. 따라서 ReadByte함수로 첫 번째 byte를 읽어오면 66이 return 될 것 입니다. 또 그 다음 호출에는 물론 77이 return되겠지요. 하지만 이 hex값들이 ascii로 대응된다고 해서 반드시 의미가 있는 data는 아닙니다. 단지 BMP파일 포맷이 가장 처음 2 byte는 66 77로 하도록 정의하고 있는 것뿐입니다. 다음은 BMP파일 포맷이 정의하고 있는 header의 구조입니다.



header에서 우리에게 의미를 가지는 data는 가로(width), 세로(height)와 유효 pixel data의 시작점(offset)입니다. 사실 Windows 그림판등의 일반적인 그래픽 편집 프로그램은 BMP파일을 저장할 때 유효 pixel data를 header 바로 다음에 위치시키므로 보통 data 시작점은 54가 됩니다. 그러나 만일의 경우를 생각해서 반드시 시작점을 header에서 읽어서 처리해주도록 합시다.

BMP파일은 유효 pixel data가 1 pixel당 3 byte로 구성되어 있습니다. 즉 3 byte가 1 pixel을 의미합니다. 3 byte의 값은 각각 red, green, blue (RGB value)를 의미합니다. 우리가 heightmap에서 높이값으로 사용하는 값은 각 pixel의 black ~ white사이 255단계 grayscale 값이기 때문에 이 RGB색상값을 grayscale로 바꾸어 사용해야 합니다. 바꾸는 방법은 간단하게 red, green, blue 세 색상값을 모두 더한 후 2의 3승, 즉 8로 나누어 주면 grayscale값이 됩니다.

이제 새 Windows Game 프로젝트를 생성하고 이전 tutorial에서 필요한 파일들을 복사해 옵시다.

가장 먼저 할 일은 당연히 BMP파일로부터 height값을 읽어오는 것 입니다. header에서 필요한 정보를 읽고 그 정보에 따라 유효 pixel data를 읽어서 grayscale로 변환해야 합니다. 다음 BMP파일을 사용하겠습니다. 물론 직접 만들어서 사용해도 상관없습니다.



BMP파일을 프로젝트 폴더에 위치시키고 해당 파일을 읽기 위해 LoadHeightData함수의 내용을 다음과 같이 수정 합시다.

 private void LoadHeightData()
 {
     FileStream fs = new FileStream("../../../heightmap.bmp", FileMode.Open, FileAccess.Read);
     BinaryReader br = new BinaryReader(fs);
 
     int offset;
 
     br.BaseStream.Seek(10, SeekOrigin.Current);
     offset = (int)br.ReadUInt32();
 
     br.BaseStream.Seek(4, SeekOrigin.Current);
     WIDTH = (int)br.ReadUInt32();
     HEIGHT = (int)br.ReadUInt32();
 
     br.BaseStream.Seek(offset - 26, SeekOrigin.Current);
     heightdata = new int[WIDTH, HEIGHT];
     for (int y = 0; y < HEIGHT; y++)
     {
         for (int x = 0; x < WIDTH; x++)
         {
             int height = ((int)(br.ReadByte()) + (int)(br.ReadByte()) + (int)(br.ReadByte())) / 8;
             heightdata[WIDTH - 1 - x, HEIGHT - 1 - y] = height;
         }
     }
 
     br.Close();
 }

먼저, 읽을 파일 경로를 다운받은 BMP파일의 경로로 바꾸어 줍니다.

 FileStream fs = new FileStream("../../../heightmap.bmp", FileMode.Open, FileAccess.Read);
 BinaryReader br = new BinaryReader(fs);

다음으로 유효 pixel data의 시작 위치를 저장해 둘 공간이 필요하므로 offset이라는 지역 변수를 선언했습니다.

 int offset;

그리고 파일 포인터를 header의 유효 pixel data 시작 위치를 지정하는 위치(seek 10)로 옮기고 ReadUInt32함수로 4byte를 읽어 offset에 저장시킵니다. (그림에서는 11-14이지만 파일 포인터는 0번부터 시작하므로 사실 10-13입니다. 다른 위치들도 마찬가지 입니다.)

 br.BaseStream.Seek(10, SeekOrigin.Current);
 offset = (int)br.ReadUInt32();

다음으로 이미지의 가로 크기와 세로 크기를 연속으로 읽어오기 위해 파일 포인터를 추가로 4만큼 움직여 가로 크기를 지정하는 위치(seek 14)에 위치시킵니다. ReadUInt32함수를 2번 호출하여 4byte씩 가로 크기와 세로 크기 전역 변수에 각각 저장시킵니다.

 br.BaseStream.Seek(4, SeekOrigin.Current);
 WIDTH = (int)br.ReadUInt32();
 HEIGHT = (int)br.ReadUInt32();

offset 변수에 저장된 유효 pixel data 시작 위치로 파일 포인터를 이동시키고 이미지의 가로 크기와 세로 크기만큼 배열을 선언합니다. 그리고 해당 pixel의 RGB색상값을 grayscale로 normalize 시킨 후 배열의 해당 위치에 저장합니다.

 br.BaseStream.Seek(offset - 26, SeekOrigin.Current);
 heightdata = new int[WIDTH, HEIGHT];
 for (int y = 0; y < HEIGHT; y++)
 {
     for (int x = 0; x < WIDTH; x++)
     {
         int height = ((int)(br.ReadByte()) + (int)(br.ReadByte()) + (int)(br.ReadByte())) / 8;
         heightdata[WIDTH - 1 - x, HEIGHT - 1 - y] = height;
     }
 }

이제 모든 vertex가 준비되었습니다만, heightmap 이미지의 가로, 세로 크기가 이전보다 훨씬 커졌기 때문에 기존 카메라의 위치에서는 전부 보이지 않습니다. 멀리서 전체를 바라볼 수 있도록 SetupCamera함수에서 카메라의 위치를 옮기고 카메라가 볼 수 있는 최대 거리도 늘려주도록 합시다.

 Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(80.0f, 0.0f, 160.0f), new Vector3(-20.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f));
 Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Window.ClientBounds.Width / Window.ClientBounds.Height, 1.0f, 250.0f);

이제 디버그(F5)키를 눌러 그 결과를 확인해봅시다.






Google
 
Webwww.riemers.net


If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !



- Website design & XNA + DirectX code : Riemer Grootjans -
©2003 - 2011 Riemer Grootjans
Translations

This site in English
This site in Korean
This site in Czech

Microsoft MVP Award



2007 - 2011 MVP Award
DirectX - XNA

Contents

[Tutorials]
[XNA in C#]
Series 1: 지형
XNA의 시작
이펙트 파일
첫 번째 삼각형
월드 좌표계
회전, 위치 변환
인덱스
지형 생성의 기본
파일로부터 지형 생성
키보드 입력
[tut10]
[tut11]
[tut12]
[tut13]
-- Expand all --


Thank you!

Support this site --
any amount is welcome !

Stay up-to-date

I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!