글
Technical Article/펌 2003. 12. 9. 09:503d 디바이스에서 한글 출력
작성자 : noerror
E 메일 : noerror@hitel.net
조회수 : 1725
작성일 : 2003-02-10 06:29:14
파아일 : hangul.zip
제에목 : 3d 디바이스에서 한글 출력
가볍게 일반적인 폴리곤 렌더링 환경에서 한글 출력하는 방법을 정리해보도록 하겠습니다.
1. 한글 출력
기본적인 컨셉은 플렛폼과 무관하게 적용 가능한 일반적인 방법의 한글 출력입니다. (d3d 전용, ps2 전용식이 아닌...)
한글은 영문과 다르게 글자의 조합이 많아서, 영문자처럼 글자 이미지를 모두 텍스처에 올려놓고 출력하기에 무리가 있습니다. 대부분 텍스처에 출력할 내용을 조합해서 그린 후에 화면에 해당 텍스처를 찍어주는 방식을 사용하는데, 이 방법에 대해서 가볍게 접근해보겠습니다.
직접 비디오 프레임 메모리에 접근하지 않아도 되기 때문에 대부분의 플렛폼에서 적용 가능하고, 가속을 받을 수 있기 때문에 속도 또한 빨라서 많이 사용되고 있습니다.
2. 한글 방식 선택
일반적으로 한글의 표현 방식은 조합형과 완성형으로 나눌 수 있습니다. 전자의 경우에는 각 조합별로 초성, 중성, 종성 세트를 가지고 글자를 조합해서 구현할 수 있으며, 후자는 모든 글자의 이미지 세트를 이용해서 구현할 수 있습니다. 각 장단점이 있지만 완성형 방식을 사용하기로 하겠습니다.
구지 제가 완성형을 사용하자고 얘기하고 이유는 아래와 같습니다.
- 일반적으로 완성형 폰트의 이미지가 조합형의 것보다 아름답다
논란의 여지는 있지만, 조합형 한글 폰트의 경우 과거 유행하던 16 x 16 크기의 폰트 이외에는 구하기도 힘들고, 좋은 폰트를 찾기가 쉽지 않기 때문에 상대적으로 퀄리티 높은 폰트들이 많이 있습니다. 완성형은 조합에 따른 각 벌을 고려하지 않아도 되기 때문에 이미지를 쉽게 얻어낼 수 있기 때문에, 윈도우상에서 다양한 크기의 벡터 폰트를 이미지로 변환하여 사용할 수 있고, 퀄리티 높은 상용폰트를 구입했을 때 별도의 작업없이 그대로 사용할 수도 있습니다.
그리고 대부분의 조합형 폰트는 단색 모노지만, 완성형은 간단한 프로그래밍으로 안티알리어싱된 폰트를 얻을 수 있습니다.
- 구현과 확장이 용이하다
완성형을 출력하고자 할 때는 한글 코드로 폰트의 인덱스 얻어내서 해당 문자를 출력하기만 하면 되기 때문에 별도의 조합 루틴이 필요없어 루틴이 간단해집니다. 그리고 단순히 코드와 폰트가 매칭되는 방식이기 때문에 중국어/일본어등으로 확장하고자 할 때 추가적인 작업이 많이 줄어듭니다.
- 실제로 용량이 크지 않다
저도 처음에 망설였던 것은 용량 문제였습니다만 실제로 폰트의 이미지는 부담이 될 정도의 크기는 아닙니다. 예를 들어 굴림12 폰트를 단색으로 메모리에 가지고 있을 때 필요한 메모리는 약 50 kb 입니다. 일반적인 16x16 조합형 폰트은 20k 보다 작지만 그 차이는 크지 않다고 할 수 있습니다. (20 메가와 50메가의 차이라면 심각하게 고려해봐야 할 것입니다만...) 퀄리티 좋은 20 크기의 궁서 4 그레이 폰트의 경우 약 270 kb 정도입니다.
이와 같은 이유로 완성형을 추천하지만 실제 구현에선 텍스처에 한글자 한글자 찍는 부분이후의 프로세스는 동일하기 때문에, 조합형을 선택한다고 해서 작업이 크게 틀려지지 않습니다.
3. 폰트 이미지 만들기
윈도우 환경에서 완성형 폰트는 쉽게 얻어낼 수 있습니다. 기본 API 의 TextOut 으로 출력할 수 있기 때문입니다. 비트맵 영역을 만들고 그곳에 한글자씩 출력해서 GetPixel 로 색을 읽는 식으로 간단하게 구현가능합니다. 첨부된 압축파일에 간단하게 만든 폰트 생성 프로그램의 소스를 참고하세요.
윈도우의 설정중에 "Smooth edges of screen fonts" 라는 옵션을 켤 경우 폰트는 안티알리어싱된 상태로 출력됩니다. 첨부된 소스의 샘플 폰트 익스포터에서는 이 이미지를 자체적인 폰트 형태로 저장하거나 이미 볼 수 있도록 bmp 로 출력해 줍니다.
경험해보시면 아시겠지만, 안티알리어싱이 된다고는 하지만 포토샵처럼 부드럽게 안티알리어싱되어 나오지 않습니다. (당연한 얘기지만 MS 워드도 캡쳐해서 보면 마찬가지임을 알 수 있습니다.) 화이트 데이란 게임을 만들 때는, 원하는 폰트사이즈보다 약 2배에서 4배정도의 큰 문자를 비트맵에 찍은 후에 다시 1/2 이나 1/4 사이즈로 필터링하며 리사이즈해서 비교적 양호한 폰트를 얻었습니다. (셈플에도 이것이 구현되어 있습니다만 적용하려면 winmain.cpp 에 있는 SCALE 매크로를 수정한 후에 다시 컴파일해야 합니다.)
index = (codehigh - 0xb0) * 94 + (codelow - 0xa1) + firstidx
관련 자료를 찾아보시면 알겠지만 일반적인 한글 코드로 인덱스를 구하는 공식은 위와 같습니다. 하지만 여기서 좀 다른 방식을 제안합니다.
위의 방식을 사용할 경우 다른 언어로 확장할 경우 인덱스 구하는 공식이 갱신되어야 하는 번거로움이 있습니다. 다국어를 고려할 경우 공식을 정의할 수 있게 만드는 것도 좋은 방법이지만 더욱 쉽고 간단한 방법으로 참조테이블을 만들어 사용할 것을 제안합니다.
인덱스에 해당하는 문자코드 리스트를 폰트에 같이 넣는 것입니다.
for(idx=0; refcode[idx] != code; idx++) ;
그러면 위와 같은 아주 간단한 방식으로 인덱스를 얻어낼 수 있을 것입니다. 일반적인 한글 코드의 영역이 0xb0a1 에서 0xc8fe 뿐 아니라 모든 영역을 사용할 수 있기 때문에 한자를 추가하거나 일어 / 중국어등의 폰트등도 만들 수 있습니다. 폰트의 코드가 일치하는 글자를 출력하는 식이기 때문에 텍스트가 일어이고 폰트가 일어이면 출력 루틴을 전혀 고치지 않고도 처리가 가능해집니다. (unicode 출력하는 것도 필요하다면 구현 가능할 것입니다. )
단, 위의 방식은 글자를 일일이 찾아야 하기 때문에 비효율적인데, 샘플에서의 폰트처럼 폰트 이미지들이 코드 순서대로 소팅되어 있다면 이진 검색으로 빠르게 해당 인덱스를 찾을 수 있습니다.
코드는 첫바이트가 의미상 일반적인 상위 비트에 해당하기 때문에 (bigendian 으로 읽은 것 처럼) 상하위 바이트를 바꿔서 코드를 바꾸고 있으니 참고하세요.
폰트를 만드는 샘플 소스에서 ExportFontThread 부분에 한글 코드들을 추가하는 부분이 있는 데, 그 부분을 수정하면 일본어나 중국어 코드들의 해당 폰트도 얻을 수 있을 것입니다. 그 소스에는 한글 폰트 (0xb0a1 ~ 0xc8fe)와 기역 같은 미완성된 낯자들을 포함하는 글자 영역(0xa4a1 에서 0xa4fe)의 폰트들을 출력하도록 되어 있습니다. (낯자들을 추가하면 IME 등의 입력중인 글자 코드도 화면에 출력할 수 있습니다.)
폰트는 그냥 간단하게 만들기 위해 4칼라로 고정했고, 폰트 영역 최적화 등은 하지 않았고, 툴도 무지막지하게 간단하게 작성했습니다. (툴 만들기엔 영 소질이 없어서...)
4. 폰트 출력하기
폰트를 텍스처에 찍고 그 영역을 저장해서 화면에 출력만 하면 됩니다만 한글자 한글자를 사전식으로 텍스처에 적어놓고 폴리곤화해서 찍는 방식이 아니라면 문제가 생깁니다. (일반적으로는 그 방법은 많이는 사용하지 않는 거 같습니다. - 100글자만 되도 최소한 삼각형 200개는 찍어야 된다는 계산이 되니까 경우에 따라선 좀 비효율적이 될 수도 있습니다.)
당연한 얘기지만 각 문장별로 텍스처를 할당할 수는 없기 때문에, 몇 장의 텍스처를 작업용으로 할당해서 빡빡하게 순서대로 적어야 할 것입니다. (이상적인 것은 가장 적은 텍스처에 가장 많은 글자를 적는 것일 겁니다.) 이때 다음과 같은 문제가 발생합니다.
- 문장이 작업용 텍스처의 가로 길이보다 길다
- 문장이 텍스처 다음 페이지로 넘어간다.
텍스처의 크기를 또 무한정 크게 잡을 수도 없기 때문에 첫번째 문제가 발생합니다. (가로가 20픽셀정도 되는 글자라면 10자만 되어도 200픽셀이 되기 때문에, 텍스처 가로 사이즈 이하로 문장길이를 제한하면 큰 제약이 되겠죠.)
당연한 얘기지만 문장을 여러 개로 잘라야 합니다. 만약 출력하고자 하는 내용이 "평생공짜왕창할인"인데, 6글자정도밖에 출력할 수 없다면
++++++++++++
++++++++++++
평생공짜왕창
할인++++++++
++++++++++++
식으로 텍스처에 쓰고 "평생공짜왕창"와 "할인"을 찍어야 할 것입니다. 여기서 자르는 방식은 두 가지로 나눌 수 있습니다. 픽셀단위로 자르는 방식과, 문자단위로 자르는 방식입니다.
만약 "아"란 글자에서 잘린다면 전자의 방식은 그 라인에 'ㅇ'이 다음 라인에 'ㅏ'가 될 수 있는 것이고, 후자의 방식은 "아" 전까지가 그 라인이고 "아"를 포함한 나머지 문장이 다음 라인이 되는 것입니다. 전자는 텍스처에 가로로 꽉꽉 채울 수 있기 때문에 더 많은 내용을 포함할 수 있지만 출력시 정교하게 연결하기가 쉽지 않다는 단덤이 있습니다. (첨부된 셈플에선 비교해볼 수 있도록 두 가지 모두 구현해 놓았는 데, _SPLIT_CHAR_ 를 정의하면 전자의 방식으로 처리됩니다.)
계속해서 다음 라인으로 넘어가다 보면 더 이상 텍스처에 적을 곳이 없는 경우가 생기는데, 이 때 앞에서 얘기한 두 번째 문제가 발생합니다. 이 경우에는 다음 텍스처에서 그리기를 계속하면 됩니다.
여기선 기본적인 매니징을 해야 하는 데, 만약 NPC가 10분동안 계속해서 얘기를 한다고 했을 때 만약 출력하는 모든 내용을 텍스처에 적으려면 엄청나게 많은 텍스처가 필요할 것입니다. 물론 그럴 필요가 없겠고 다음과 같은 원칙을 두면 될 것이다.
- 이미 텍스처에 적은 내용은 다시 올리지 않고 이전에 작성한 것을 화면에 표시한다
- 과거에 찍은 것들 중에 현재 사용되지 않는 것은 삭제한다
이런 식으로 텍스처를 반복해서 사용하면 적은 수의 텍스처만 사용해서 처리를 할 수 있습니다. (최대 문장이 꽉차는 텍스처보다 한 장정도 많은 것이 적당하다고 생각됩니다.) 아주 난이도가 있는 것은 아니지만 문장의 길이를 미리 예측할 수 없는 경우- 온라인 게임처럼 -에는 좀 더 신중한 처리를 해야 할 것입니다. 텍스처는 3장인데 4장분을 출력하려고 하면 문제가 반드시 생기기 때문입니다. (셈플에서는 m_sparetex 라는 것으로 처리를 했습니다만 깔끔하게는 처리하기는 힘드네요.)
여기서 "한 장 크게 잡고 텍스처 갱신하고 화면에 찍고, 다시 그 위에 찍고 다시 화면에 찍고 하면 안되냐 ?"고 하실 수 있는 데, 특정 하드웨어가 아니라면 정상적인 실행을 보장할 수 없는 방법입니다. (마지막에 갱신된 내용으로 모두 찍힐 가능성이 높습니다.)
텍스처에 올려서 화면에 찍을 때는 잘린 걸 순서대로 잘 이어 붙이는 거 외엔 특별히 추가적으로 생각할 것이 없지만, 찍을 때 텍스처 블랜드 방식을 잘 생각해야 합니다.
밝은 글씨를 찍을 때는 가산 (DEST + SRC[tex * color]) 모드로 찍으면 됩니다. 기본적으로 텍스처에 글자는 흰색, 바탕은 검정색이므로 더하게 되면 검정색 부분은 찍히지 않으므로 흰색 글씨 부분만 찍히게 됩니다. 그리고 텍스처가 흰색이므로 원하는 색을 곱해주면 여러가지 색으로 찍을 수 있습니다. (1 * color = color)
그리고 어둡게 찍고자 한다면 인버스 멀티플라이 (DEST * (1 - SRC[tex * color])) 로 찍으면 됩니다. 실제로 인버스 멀티플라이 모드에선 검정색 부분이 흰색이 되어 찍히지 않고 (DEST * (1 - 0) = DEST) 흰색이 검정색이 되어 그 부분이 어두워지게 됩니다. (말로는 좀 힘드네요.) 만약 흰색 바탕에 초록색의 글씨를 쓰고 싶다면 rgb는 0x00ff00 을 인버스한 0xff00ff 값으로 설정해야 합니다.
인버스 멀티플라이 모드는 어두워지는 부분에 색을 설정할 수 있기 때문에 그림자 찍을 때 많이 사용됩니다. (색을 안 줄 경우 그냥 멀티플라이 (DEST * SRC) 로도 처리할 수 있기 때문에 특별한 의도가 아니면 많이는 사용되지 않는 블랜딩 방법이긴 합니다.)
5. 셈플 설명
fontexp 프로젝트는 폰트를 만드는 파일로, 흐름만 참고하신다면 큰 어려움없이 필요하신 것들을 만드실 수 있을 것입니다.
hangul 프로젝트는 실제 폰트를 출력하는 셈플로 d3d 와 opengl 디바이스에 맞춰져 있습니다. 디바이스는 적당히 껴다 맞춘 것이니 그 부분에는 큰 의미를 두지 마시길 바랍니다. (테스트 코드들 적당히 지우고 하다 보니 실제로 돌아가는 거 말고는 문제가 있을 것입니다.)
실제코드는 font 쪽입니다. 내용이 섞이면 알아보기 힘들어 질 거 같아 의미별로 font 와 텍스처에 폰트를 찍는 fontworkspace 를 나누었고, 실제로 fontmanager 에서 이 두 객체를 이용해 출력하게 했습니다. (fontworkspace 하나에 font 여러 개를 사용할 수 있도록 구성했습니다. - 크게 필요한 기능은 아니지만...)
sample.cpp 에 _D3D_ 정의한 부분 삭제하고 컴파일 하면 opengl 로 작동합니다.
6. 마무리
폰트의 루틴이긴 하지만 텍스처의 일부분을 출력하는 개념의 일반적인 오버레이(2d) 루틴들과 거의 비슷합니다. 아이콘이나 상태창, 아이템들도 비슷하게 관리하고 처리할 수 있을 겁니다. (가능한 화면에 찍지 않는 것들은 텍스처에 올리지 않는 것이 전체적인 성능 향상에 도움이 될 것입니다.)
그 정도까지 필요할까 싶지만 동적으로 폰트에 문자셋을 추가하는 식으로 루틴을 작성하고, IME 루틴을 잘 조합해두면 전혀 모르는 나라에서도 게임을 그대로 즐길 수 있을 지도 모르곘습니다.
ps. 예전부터 d3d 만 접하다 이번 기회에 셈플 만들면서 opengl 해 봤는 데 API가 쉽고 간단하게 잘 되어있네요. (저에겐 직관적이고 좋네요.) 앞으론 공부하고 테스트할 땐 opengl을 더 선호할 거 같습니다. 좋습니다.
| 이전글 | 다음글 | 리스트로 | 답글쓰기 | 수정 | 삭제 |
noerror 폰트 뽑은 셈플 : http://digibath.com/noerror/download/gulim12.gif 2003/04/13
noerror 버퍼에 작성하는 두가지 방법 셈플 : http://digibath.com/noerror/download/font.gif 2003/04/13
아노아 감사합니다 ㅠ _-) 잘쓰겠습니다. 2003/05/20
흠.. 뷁이 출력이...... 2003/08/05
noerror http://www.w3c.or.kr/i18n/hangul-i18n/ko-code.html 페이지 Microsoft의 통합형 한글에 추가코드까지 추출한 폰트를 사용해야 찍힙니다 ^^ 2003/08/25
noerror 참고로, 일본어 출력의 경우 http://www.kanzaki.com/docs/jcode.html 참고하셔서 JIS 코드 영역을 뽑으면 됩니다 2003/08/25
perpet 폰트사이즈가 다른것을 2개를 쓰려고 하면 font 클래스와 fontworkspace 클래스를 각각 2개씩만들어야 하고. fontworkspace클래스 하나에 font클래스2개식으로 하려면 폰트높이값이 가변이기때문에 쓸때마다 항상높이를 체크해서 처리를 해주어야 하네요..참고하세요..하여튼 잘쓰고 있습니다..감사..^^ 2003/09/08
자갈공명 파아일, 제에목...^^;;; 드래그해서 퍼가다가 ...잘쓰겠습니다..^^ 2003/09/24
[출처]
http://www.gamecode.org/article.php3?no=1501&page=0¤t=0&field=tip
E 메일 : noerror@hitel.net
조회수 : 1725
작성일 : 2003-02-10 06:29:14
파아일 : hangul.zip
제에목 : 3d 디바이스에서 한글 출력
가볍게 일반적인 폴리곤 렌더링 환경에서 한글 출력하는 방법을 정리해보도록 하겠습니다.
1. 한글 출력
기본적인 컨셉은 플렛폼과 무관하게 적용 가능한 일반적인 방법의 한글 출력입니다. (d3d 전용, ps2 전용식이 아닌...)
한글은 영문과 다르게 글자의 조합이 많아서, 영문자처럼 글자 이미지를 모두 텍스처에 올려놓고 출력하기에 무리가 있습니다. 대부분 텍스처에 출력할 내용을 조합해서 그린 후에 화면에 해당 텍스처를 찍어주는 방식을 사용하는데, 이 방법에 대해서 가볍게 접근해보겠습니다.
직접 비디오 프레임 메모리에 접근하지 않아도 되기 때문에 대부분의 플렛폼에서 적용 가능하고, 가속을 받을 수 있기 때문에 속도 또한 빨라서 많이 사용되고 있습니다.
2. 한글 방식 선택
일반적으로 한글의 표현 방식은 조합형과 완성형으로 나눌 수 있습니다. 전자의 경우에는 각 조합별로 초성, 중성, 종성 세트를 가지고 글자를 조합해서 구현할 수 있으며, 후자는 모든 글자의 이미지 세트를 이용해서 구현할 수 있습니다. 각 장단점이 있지만 완성형 방식을 사용하기로 하겠습니다.
구지 제가 완성형을 사용하자고 얘기하고 이유는 아래와 같습니다.
- 일반적으로 완성형 폰트의 이미지가 조합형의 것보다 아름답다
논란의 여지는 있지만, 조합형 한글 폰트의 경우 과거 유행하던 16 x 16 크기의 폰트 이외에는 구하기도 힘들고, 좋은 폰트를 찾기가 쉽지 않기 때문에 상대적으로 퀄리티 높은 폰트들이 많이 있습니다. 완성형은 조합에 따른 각 벌을 고려하지 않아도 되기 때문에 이미지를 쉽게 얻어낼 수 있기 때문에, 윈도우상에서 다양한 크기의 벡터 폰트를 이미지로 변환하여 사용할 수 있고, 퀄리티 높은 상용폰트를 구입했을 때 별도의 작업없이 그대로 사용할 수도 있습니다.
그리고 대부분의 조합형 폰트는 단색 모노지만, 완성형은 간단한 프로그래밍으로 안티알리어싱된 폰트를 얻을 수 있습니다.
- 구현과 확장이 용이하다
완성형을 출력하고자 할 때는 한글 코드로 폰트의 인덱스 얻어내서 해당 문자를 출력하기만 하면 되기 때문에 별도의 조합 루틴이 필요없어 루틴이 간단해집니다. 그리고 단순히 코드와 폰트가 매칭되는 방식이기 때문에 중국어/일본어등으로 확장하고자 할 때 추가적인 작업이 많이 줄어듭니다.
- 실제로 용량이 크지 않다
저도 처음에 망설였던 것은 용량 문제였습니다만 실제로 폰트의 이미지는 부담이 될 정도의 크기는 아닙니다. 예를 들어 굴림12 폰트를 단색으로 메모리에 가지고 있을 때 필요한 메모리는 약 50 kb 입니다. 일반적인 16x16 조합형 폰트은 20k 보다 작지만 그 차이는 크지 않다고 할 수 있습니다. (20 메가와 50메가의 차이라면 심각하게 고려해봐야 할 것입니다만...) 퀄리티 좋은 20 크기의 궁서 4 그레이 폰트의 경우 약 270 kb 정도입니다.
이와 같은 이유로 완성형을 추천하지만 실제 구현에선 텍스처에 한글자 한글자 찍는 부분이후의 프로세스는 동일하기 때문에, 조합형을 선택한다고 해서 작업이 크게 틀려지지 않습니다.
3. 폰트 이미지 만들기
윈도우 환경에서 완성형 폰트는 쉽게 얻어낼 수 있습니다. 기본 API 의 TextOut 으로 출력할 수 있기 때문입니다. 비트맵 영역을 만들고 그곳에 한글자씩 출력해서 GetPixel 로 색을 읽는 식으로 간단하게 구현가능합니다. 첨부된 압축파일에 간단하게 만든 폰트 생성 프로그램의 소스를 참고하세요.
윈도우의 설정중에 "Smooth edges of screen fonts" 라는 옵션을 켤 경우 폰트는 안티알리어싱된 상태로 출력됩니다. 첨부된 소스의 샘플 폰트 익스포터에서는 이 이미지를 자체적인 폰트 형태로 저장하거나 이미 볼 수 있도록 bmp 로 출력해 줍니다.
경험해보시면 아시겠지만, 안티알리어싱이 된다고는 하지만 포토샵처럼 부드럽게 안티알리어싱되어 나오지 않습니다. (당연한 얘기지만 MS 워드도 캡쳐해서 보면 마찬가지임을 알 수 있습니다.) 화이트 데이란 게임을 만들 때는, 원하는 폰트사이즈보다 약 2배에서 4배정도의 큰 문자를 비트맵에 찍은 후에 다시 1/2 이나 1/4 사이즈로 필터링하며 리사이즈해서 비교적 양호한 폰트를 얻었습니다. (셈플에도 이것이 구현되어 있습니다만 적용하려면 winmain.cpp 에 있는 SCALE 매크로를 수정한 후에 다시 컴파일해야 합니다.)
index = (codehigh - 0xb0) * 94 + (codelow - 0xa1) + firstidx
관련 자료를 찾아보시면 알겠지만 일반적인 한글 코드로 인덱스를 구하는 공식은 위와 같습니다. 하지만 여기서 좀 다른 방식을 제안합니다.
위의 방식을 사용할 경우 다른 언어로 확장할 경우 인덱스 구하는 공식이 갱신되어야 하는 번거로움이 있습니다. 다국어를 고려할 경우 공식을 정의할 수 있게 만드는 것도 좋은 방법이지만 더욱 쉽고 간단한 방법으로 참조테이블을 만들어 사용할 것을 제안합니다.
인덱스에 해당하는 문자코드 리스트를 폰트에 같이 넣는 것입니다.
for(idx=0; refcode[idx] != code; idx++) ;
그러면 위와 같은 아주 간단한 방식으로 인덱스를 얻어낼 수 있을 것입니다. 일반적인 한글 코드의 영역이 0xb0a1 에서 0xc8fe 뿐 아니라 모든 영역을 사용할 수 있기 때문에 한자를 추가하거나 일어 / 중국어등의 폰트등도 만들 수 있습니다. 폰트의 코드가 일치하는 글자를 출력하는 식이기 때문에 텍스트가 일어이고 폰트가 일어이면 출력 루틴을 전혀 고치지 않고도 처리가 가능해집니다. (unicode 출력하는 것도 필요하다면 구현 가능할 것입니다. )
단, 위의 방식은 글자를 일일이 찾아야 하기 때문에 비효율적인데, 샘플에서의 폰트처럼 폰트 이미지들이 코드 순서대로 소팅되어 있다면 이진 검색으로 빠르게 해당 인덱스를 찾을 수 있습니다.
코드는 첫바이트가 의미상 일반적인 상위 비트에 해당하기 때문에 (bigendian 으로 읽은 것 처럼) 상하위 바이트를 바꿔서 코드를 바꾸고 있으니 참고하세요.
폰트를 만드는 샘플 소스에서 ExportFontThread 부분에 한글 코드들을 추가하는 부분이 있는 데, 그 부분을 수정하면 일본어나 중국어 코드들의 해당 폰트도 얻을 수 있을 것입니다. 그 소스에는 한글 폰트 (0xb0a1 ~ 0xc8fe)와 기역 같은 미완성된 낯자들을 포함하는 글자 영역(0xa4a1 에서 0xa4fe)의 폰트들을 출력하도록 되어 있습니다. (낯자들을 추가하면 IME 등의 입력중인 글자 코드도 화면에 출력할 수 있습니다.)
폰트는 그냥 간단하게 만들기 위해 4칼라로 고정했고, 폰트 영역 최적화 등은 하지 않았고, 툴도 무지막지하게 간단하게 작성했습니다. (툴 만들기엔 영 소질이 없어서...)
4. 폰트 출력하기
폰트를 텍스처에 찍고 그 영역을 저장해서 화면에 출력만 하면 됩니다만 한글자 한글자를 사전식으로 텍스처에 적어놓고 폴리곤화해서 찍는 방식이 아니라면 문제가 생깁니다. (일반적으로는 그 방법은 많이는 사용하지 않는 거 같습니다. - 100글자만 되도 최소한 삼각형 200개는 찍어야 된다는 계산이 되니까 경우에 따라선 좀 비효율적이 될 수도 있습니다.)
당연한 얘기지만 각 문장별로 텍스처를 할당할 수는 없기 때문에, 몇 장의 텍스처를 작업용으로 할당해서 빡빡하게 순서대로 적어야 할 것입니다. (이상적인 것은 가장 적은 텍스처에 가장 많은 글자를 적는 것일 겁니다.) 이때 다음과 같은 문제가 발생합니다.
- 문장이 작업용 텍스처의 가로 길이보다 길다
- 문장이 텍스처 다음 페이지로 넘어간다.
텍스처의 크기를 또 무한정 크게 잡을 수도 없기 때문에 첫번째 문제가 발생합니다. (가로가 20픽셀정도 되는 글자라면 10자만 되어도 200픽셀이 되기 때문에, 텍스처 가로 사이즈 이하로 문장길이를 제한하면 큰 제약이 되겠죠.)
당연한 얘기지만 문장을 여러 개로 잘라야 합니다. 만약 출력하고자 하는 내용이 "평생공짜왕창할인"인데, 6글자정도밖에 출력할 수 없다면
++++++++++++
++++++++++++
평생공짜왕창
할인++++++++
++++++++++++
식으로 텍스처에 쓰고 "평생공짜왕창"와 "할인"을 찍어야 할 것입니다. 여기서 자르는 방식은 두 가지로 나눌 수 있습니다. 픽셀단위로 자르는 방식과, 문자단위로 자르는 방식입니다.
만약 "아"란 글자에서 잘린다면 전자의 방식은 그 라인에 'ㅇ'이 다음 라인에 'ㅏ'가 될 수 있는 것이고, 후자의 방식은 "아" 전까지가 그 라인이고 "아"를 포함한 나머지 문장이 다음 라인이 되는 것입니다. 전자는 텍스처에 가로로 꽉꽉 채울 수 있기 때문에 더 많은 내용을 포함할 수 있지만 출력시 정교하게 연결하기가 쉽지 않다는 단덤이 있습니다. (첨부된 셈플에선 비교해볼 수 있도록 두 가지 모두 구현해 놓았는 데, _SPLIT_CHAR_ 를 정의하면 전자의 방식으로 처리됩니다.)
계속해서 다음 라인으로 넘어가다 보면 더 이상 텍스처에 적을 곳이 없는 경우가 생기는데, 이 때 앞에서 얘기한 두 번째 문제가 발생합니다. 이 경우에는 다음 텍스처에서 그리기를 계속하면 됩니다.
여기선 기본적인 매니징을 해야 하는 데, 만약 NPC가 10분동안 계속해서 얘기를 한다고 했을 때 만약 출력하는 모든 내용을 텍스처에 적으려면 엄청나게 많은 텍스처가 필요할 것입니다. 물론 그럴 필요가 없겠고 다음과 같은 원칙을 두면 될 것이다.
- 이미 텍스처에 적은 내용은 다시 올리지 않고 이전에 작성한 것을 화면에 표시한다
- 과거에 찍은 것들 중에 현재 사용되지 않는 것은 삭제한다
이런 식으로 텍스처를 반복해서 사용하면 적은 수의 텍스처만 사용해서 처리를 할 수 있습니다. (최대 문장이 꽉차는 텍스처보다 한 장정도 많은 것이 적당하다고 생각됩니다.) 아주 난이도가 있는 것은 아니지만 문장의 길이를 미리 예측할 수 없는 경우- 온라인 게임처럼 -에는 좀 더 신중한 처리를 해야 할 것입니다. 텍스처는 3장인데 4장분을 출력하려고 하면 문제가 반드시 생기기 때문입니다. (셈플에서는 m_sparetex 라는 것으로 처리를 했습니다만 깔끔하게는 처리하기는 힘드네요.)
여기서 "한 장 크게 잡고 텍스처 갱신하고 화면에 찍고, 다시 그 위에 찍고 다시 화면에 찍고 하면 안되냐 ?"고 하실 수 있는 데, 특정 하드웨어가 아니라면 정상적인 실행을 보장할 수 없는 방법입니다. (마지막에 갱신된 내용으로 모두 찍힐 가능성이 높습니다.)
텍스처에 올려서 화면에 찍을 때는 잘린 걸 순서대로 잘 이어 붙이는 거 외엔 특별히 추가적으로 생각할 것이 없지만, 찍을 때 텍스처 블랜드 방식을 잘 생각해야 합니다.
밝은 글씨를 찍을 때는 가산 (DEST + SRC[tex * color]) 모드로 찍으면 됩니다. 기본적으로 텍스처에 글자는 흰색, 바탕은 검정색이므로 더하게 되면 검정색 부분은 찍히지 않으므로 흰색 글씨 부분만 찍히게 됩니다. 그리고 텍스처가 흰색이므로 원하는 색을 곱해주면 여러가지 색으로 찍을 수 있습니다. (1 * color = color)
그리고 어둡게 찍고자 한다면 인버스 멀티플라이 (DEST * (1 - SRC[tex * color])) 로 찍으면 됩니다. 실제로 인버스 멀티플라이 모드에선 검정색 부분이 흰색이 되어 찍히지 않고 (DEST * (1 - 0) = DEST) 흰색이 검정색이 되어 그 부분이 어두워지게 됩니다. (말로는 좀 힘드네요.) 만약 흰색 바탕에 초록색의 글씨를 쓰고 싶다면 rgb는 0x00ff00 을 인버스한 0xff00ff 값으로 설정해야 합니다.
인버스 멀티플라이 모드는 어두워지는 부분에 색을 설정할 수 있기 때문에 그림자 찍을 때 많이 사용됩니다. (색을 안 줄 경우 그냥 멀티플라이 (DEST * SRC) 로도 처리할 수 있기 때문에 특별한 의도가 아니면 많이는 사용되지 않는 블랜딩 방법이긴 합니다.)
5. 셈플 설명
fontexp 프로젝트는 폰트를 만드는 파일로, 흐름만 참고하신다면 큰 어려움없이 필요하신 것들을 만드실 수 있을 것입니다.
hangul 프로젝트는 실제 폰트를 출력하는 셈플로 d3d 와 opengl 디바이스에 맞춰져 있습니다. 디바이스는 적당히 껴다 맞춘 것이니 그 부분에는 큰 의미를 두지 마시길 바랍니다. (테스트 코드들 적당히 지우고 하다 보니 실제로 돌아가는 거 말고는 문제가 있을 것입니다.)
실제코드는 font 쪽입니다. 내용이 섞이면 알아보기 힘들어 질 거 같아 의미별로 font 와 텍스처에 폰트를 찍는 fontworkspace 를 나누었고, 실제로 fontmanager 에서 이 두 객체를 이용해 출력하게 했습니다. (fontworkspace 하나에 font 여러 개를 사용할 수 있도록 구성했습니다. - 크게 필요한 기능은 아니지만...)
sample.cpp 에 _D3D_ 정의한 부분 삭제하고 컴파일 하면 opengl 로 작동합니다.
6. 마무리
폰트의 루틴이긴 하지만 텍스처의 일부분을 출력하는 개념의 일반적인 오버레이(2d) 루틴들과 거의 비슷합니다. 아이콘이나 상태창, 아이템들도 비슷하게 관리하고 처리할 수 있을 겁니다. (가능한 화면에 찍지 않는 것들은 텍스처에 올리지 않는 것이 전체적인 성능 향상에 도움이 될 것입니다.)
그 정도까지 필요할까 싶지만 동적으로 폰트에 문자셋을 추가하는 식으로 루틴을 작성하고, IME 루틴을 잘 조합해두면 전혀 모르는 나라에서도 게임을 그대로 즐길 수 있을 지도 모르곘습니다.
ps. 예전부터 d3d 만 접하다 이번 기회에 셈플 만들면서 opengl 해 봤는 데 API가 쉽고 간단하게 잘 되어있네요. (저에겐 직관적이고 좋네요.) 앞으론 공부하고 테스트할 땐 opengl을 더 선호할 거 같습니다. 좋습니다.
| 이전글 | 다음글 | 리스트로 | 답글쓰기 | 수정 | 삭제 |
noerror 폰트 뽑은 셈플 : http://digibath.com/noerror/download/gulim12.gif 2003/04/13
noerror 버퍼에 작성하는 두가지 방법 셈플 : http://digibath.com/noerror/download/font.gif 2003/04/13
아노아 감사합니다 ㅠ _-) 잘쓰겠습니다. 2003/05/20
흠.. 뷁이 출력이...... 2003/08/05
noerror http://www.w3c.or.kr/i18n/hangul-i18n/ko-code.html 페이지 Microsoft의 통합형 한글에 추가코드까지 추출한 폰트를 사용해야 찍힙니다 ^^ 2003/08/25
noerror 참고로, 일본어 출력의 경우 http://www.kanzaki.com/docs/jcode.html 참고하셔서 JIS 코드 영역을 뽑으면 됩니다 2003/08/25
perpet 폰트사이즈가 다른것을 2개를 쓰려고 하면 font 클래스와 fontworkspace 클래스를 각각 2개씩만들어야 하고. fontworkspace클래스 하나에 font클래스2개식으로 하려면 폰트높이값이 가변이기때문에 쓸때마다 항상높이를 체크해서 처리를 해주어야 하네요..참고하세요..하여튼 잘쓰고 있습니다..감사..^^ 2003/09/08
자갈공명 파아일, 제에목...^^;;; 드래그해서 퍼가다가 ...잘쓰겠습니다..^^ 2003/09/24
[출처]
http://www.gamecode.org/article.php3?no=1501&page=0¤t=0&field=tip
![](https://lh3.googleusercontent.com/-hYZb_novCPQ/V5HuGPkGFUI/AAAAAAAAANk/f8zcKkeTBbA1A-W6yuqfk12fs8bd8FeOQCL0B/banner_468_60.png)
RECENT COMMENT