NULL에 대해

unsigned char* p = NULL;

이라는 코드는 특정한 헤더를 include하지 않는 이상은 컴파일 에러가 나는 코드이다. 하지만 우리 눈에는 코드가 아주 자연스럽게 보인다. 게다가 C에서는 0 아닌 NULL 쓰는 습관을 길러야 한다고 이야기 해왔고, 일부 기고 글에서는 무효화된 포인터가 0이라는 것은 스펙에 정의되어 있지 않으므로 항상 컴파일러가 제공하는 헤더에 정의된 NULL이라는 값만 써야 한다는 주장도 있다. 그리고 실제 컴파일러도 NULL 정의된 주소 값에 대해서는 일반 값과는 조금 다른 동작을 한다고도 알려져 있다. (casting 관련된 경우)

vc++ 정의된 stdlib.h 정의된 NULL 값을 보자. (Linux gcc라면 /usr/include/lunux/stddef.h 있다)

#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

(최신 버전의 g++에는 __null 추가된 같긴 하지만 모든 컴파일러가 __null 지원하는 것은 아니므로 일단은 예외로 하겠다)

Pascal 경우에는 nil이란 예약어가 있어서 언어적으로 0과는 구분을 있게 해주었고 서로 섞어 없도록 문법적으로 막아 놓았다. 하지만 C++ 경우에는 #define 값마저도 0 동일하다. C++ 정상적인 프로그래밍 언어라면 #include 하나 없이도 코딩이 가능해야 한다. (OS API IO등의 디바이스 제어와 관련된 부분은 제외하고) 여기서 우리는 #include 사용해서 NULL 정의를 불러 것이냐,
니면 암묵적인 NULL 통용되는 0 사용할 것이냐는 문제에 빠진다.

문제에 대한 답은 없지만, 나의 경우에는 0 사용하는 것으로 결정을 했다. 클래스 헤더 정의에서 포인터형의 멤버 변수가 있고 그것을 생성자 등에서 초기화 해야 , 그것을 위해서 때문에 #include 쓰고 싶지 않기 때문이다. 또한 순수 가상 함수를 정의하기 위한 때에도 같은 이유로 ‘= 0’ 사용한다.

Posted by 안영기

2009/03/15 07:24 2009/03/15 07:24
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/10

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

printf()의 출력이 이상하다?!

예전에 나와 같이 일하던 후임 중에 한 명이 어떤 문제를 가지고 왔다. 타겟 디바이스에서 디버깅 하려고 printf()를 사용했는데 아무래도 원하는 결과와는 다르게 나온다는 것이다. 결국 VC++에서도 동일한 문제가 나온다는 것을 확인해서 다음과 같이 문제를 간략화 시켰다.
코드
struct
{
int* a;
int* b;
int* c;
} a = {0, 0, (int*)1};

printf(
"%d, %d, %d\n", a.c, a, a.c);
결과
1, 0, 0
a.c와 a와 다시 a.c의 내용을 출력하는 예제이다. 분명 코드 상으로도 첫 번째 파라미터와 세 번째 파라미터는 모두 a.c로 동일하다. 그런데 결과는 1과 0으로 서로 다르게 나왔다.
아마도 쉽게 실수 할 수 있는 부분으로 생각되는데, 실수 하기는 쉬운데 반해 그 실수를 찾는데는 굉장히 힘들게 될지도 모르겠다.

Posted by 안영기

2009/02/23 00:10 2009/02/23 00:10
Response
0 Trackbacks , 3 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/9

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

Endian의 고려

자신이 영원히 MS Windows에서 코드를 만든다면 endian은 전혀 신경 쓰지 않아도 된다. 하지만 그렇게 만든 코드는 항상 little endian 전용이라는 꼬리표가 붙어 다녀야 제대로 된 것이다.

코드

unsigned long aaa   = 0x01FF0002;
unsigned char* pCh  = (unsigned char*)&aaa;
unsigned short* pWd = (unsigned short*)(pCh+1);
printf("+++++ %x, %x\n", *pCh, *pWd);

위의 코드는 endian이 전혀 고려되지 않은 코드이므로 일반적인 목적에서는 잘 못 만들어진 코드이다. 그래서 그 결과를 실제로 돌려보면 다음과 같다.

Little endian의 결과

+++++ 2, ff00

Big endian의 결과

+++++ 1, 00ff

Little endian의 결과 (ARM 컴파일러)

+++++ 2, 3d8f

Little endian과 big endian의 결과는 예상한 대로 서로 다르게 나타났다. 원래 메모리의 구조가 그런 것이니 이렇게 나오는 것이 맞다. 그렇기 때문에 위의 코드처럼 프로그램을 만들면 그 코드는 특정 endian에만 적용되는 코드가 되는 것이다.

그리고 여기서 말하고자 하는 것은 이것말고 또 하나가 있다. 제일 마지막에 나온 ARM 컴파일러의 little endian의 결과이다. 같은 little endian이라도 해도 chip-set이나 컴파일러가 달라지면 그 결과가 달라진다는 것을 말하기 위해서 마지막의 결과도 하나 추가했다.

ARM의 경우에는 3d8f라는 전혀 엉뚱한 값이 나타났는데 이 값은 그 결과를 예상할 수 없는 값이며 항상 바뀔 수 있다. 이런데 이 결과는 문제가 없다. 왜냐면 ARM 컴파일러의 매뉴얼에서는 이 문제에 대해서 확실히 언급을 하고 있기 때문이다. (각 자료형에 대한 align 문제이며 이것은 ARM의 동작과 관련된 문제이다) 그래서 비록 자신이 사용하는 컴파일러가 ARM용이 아니라고 해도 범용적인 코딩을 한다고 생각한다면 이런 것까지 다 고려해야 한다.

그래서 결론을 이야기 하자면, endian을 항상 고려해서 코딩을 해야 하며 모든 칩에서 호환이 가능한 문법으로 접근하자는 것이다.

Posted by 안영기

2009/02/22 23:53 2009/02/22 23:53
Response
0 Trackbacks , 4 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/8

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

g++의 버그

프로그램 개발자는 컴파일러를 의심하면 안된다. 제일 먼저 자신 탓을 하고, 하드웨어의 특성을 의심하고, 그것도 문제가 아니라면 그 때야 컴파일러를 의심해 보아야 한다.

그런데 수 많은 버그들과 씨름하다보면 가끔씩 컴파일러를 의심해야 할 때도 있다. 특히 변방의 열악한 컴파일러라면 그런 일이 좀 많긴하다. 그런데 이번에는 g++에 대해서 이야기 하고자 한다.

소스

#include <stdio.h>

int main()
{
unsigned char buffer[2] = {0, 0};
unsigned char* pByte = buffer;

*pByte = 1 + *pByte++;

printf("%d, %d\n", buffer[0], buffer[1]);

return 0;
}

이것의 결과는 어떨까. 나는 2개의 g++ 버전으로 테스트를 해보았다.

g++ 4.0.1의 결과

% g++ --version
g++ (GCC) 4.0.1 (Debian 4.0.1-2)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

% g++ q.cpp
% ./a.out
1, 0
% g++ q.cpp -O3
% ./a.out
1, 0

g++ 3.4.4의 결과

$ g++ --version
g++ (GCC) 3.4.4 (cygming special) (gdc 0.12, using dmd 0.125)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ q.cpp
$ ./a.exe
1, 0
$ g++ q.cpp -O3
$ ./a.exe
0, 1

확실히 뭔가 이상하다. 4번의 실행 결과는 모두 같아야 하는데 마지막 하나는 결과가 다르다. x86과 MIPS와 ARM의 컴파일러가 서로 결과가 다른 것은 그나마 자주 있는 일인데, 같은 x86인데도 불구하고  동일 컴파일러인데도 버전이 달라지면서 결과가 달라진다든지 최적화 옵션에 따라 결과가 달라진다든지 해서는 안될 것이다.

이 문제가 컴파일러를 만든 쪽에 보고가 된 것인지 아닌지는 알 수가 없지만, 하여간 나는 이 문제 때문에 며칠을 날려 먹었던 것이다. (x86 vs. ARM의 경우에는 컴파일러 특성으로 결과가 달라지는 것은 굉장히 흔한 경우이다. 다음 기회에 이것도 한 번 다루겠다.)

Posted by 안영기

2009/02/22 23:22 2009/02/22 23:22
Response
0 Trackbacks , 2 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/7

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

2의 보수 방식의 맹점

아래의 소스를 실행했을 때의 결과는 다음과 같다.

소스
#include <stdio.h>

union TFontStyle
{
struct
{
int useItalic: 1;
int useBold:   1;
int isFixed:   1;
int reserved:  1;
int grayLevel: 4;
int color:    24;
};
int data; 
};

int main()
{
// TFontStyle의 크기를 확인해 본다.
// union이니 보통 4bytes가 나올 것이다.
printf("sizeof(TFontStyle) = %d\n", sizeof(TFontStyle));

{
TFontStyle fontStyle;

// 전체 bit field를 초기화
fontStyle.data = 0;

// 위에서 초기화를 했으니 아마도 0이겠지.
printf("useItalic = %d\n", fontStyle.useItalic);

// 이태릭을 사용하는 것으로 설정.
fontStyle.useItalic = 1;

// 위로 설정한 대로 그대로 비교를 하자.
// 그 결과는?
if (fontStyle.useItalic == 1)
printf("That's the way!!\n");
else
printf("why??!!\n");
}

return 0;
}

결과
sizeof(TFontStyle) = 4
useItalic = 0
why|!  <-- 왜 이것이 출력되지?


딴 부분은 다 문제가 없고 제일 마지막의 if문이 문제인데, 이런 문제(2의 보수를 이용한 음수 표현)때문에 if문의 분기가 예측과는 다르게 나간다면 참으로 잡기 어려운 버그를 만들 수 있을 것이다.

Posted by 안영기

2009/02/15 09:52 2009/02/15 09:52
Response
0 Trackbacks , 5 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/6

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

각 자료형의 크기에 관한 이야기

- char, short, int, long의 크기
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)

대부분 1, 2, 4, 4가 나오며 DOS 때의 16비트 컴파일러는 1, 2, 2, 4 였던 것 같다.
64비트 컴파일러는 sizeof(int)를 원래의 의미에 맞게 8로 정의하는 것도 있으나 호환성을 위해 4로 정의 하는 것이 더 많은 것 같다.

- bool의 크기
1 <= sizeof(bool) <= sizeof(long)

VC++이나 gcc는 1이지만, ARM용 컴파일러는 4이다.
구조체 중간에 bool이 있다면 그 구조체의 크기는 예측하기가 어렵다. 특히 그 크기가 1이라면 bool이 있는 구조체 내의 위치에 따라 alignment의 영향을 받아 그 크기가 달라진다.

- wchar_t의 크기
sizeof(char) <= sizeof(wchar_t) <= sizeof(long)

UCS-2가 기본인 VC++은 2, UCS-4가 기본인 gcc는 4이다. 컴파일 옵션에 따라서 변경도 가능하다.
그리고 이것 때문에 L””로 만든 문자열의 크기도 달라진다.

- float과 double의 크기
sizeof(float) <= sizeof(double) <= sizeof(long double)

VC++에서는 각각 4, 8, 8으로 알고 있지만 다른 컴파일러에서는 확인 해 본 적은 없다.

- signed / unsigned에 따른 크기
sizeof(N) <= sizeof(signed N) <= sizeof(unsigned N)

2의 보수로서 음수를 표현하는 것이면 항상 모두 같아야 하는 것이 아닌가 생각되긴 하지만… 이건 잘 모르겠다.

- enum의 크기
보통 4로 고정되기도 하지만 ARM용 컴파일러는 최대 값에 따라 그 크기가 달라진다.
따라서 구조체 내에 enum으로 선언된 변수가 있으면 enum의 항목 추가에 따라 어느 순간 그 구조체의 크기가 달라질 수 있다는 것이다. 따라서 항상 제일 마지막 값에는 0x7FFFFFFF를 강제로 넣어 주는 것이 이 문제를 피할 수 있는 한 방법이 된다.

- char의 선언
일반적인 컴파일러는 char로 변수 선언을 하게 되면 당연히 signed가 생략된 것으로 보지만 ARM 계열의 컴파일러에서는 unsigned가 생략된 것으로 취급하는 것이 default 동작이다. 이것은 char를 더 큰 자료형으로 (자동)확장하여 연산하는 경우 찾기 어려운 오류를 만든다.

이 때는 직접 signed로 선언해 주는 방법도 있지만 컴파일 옵션에서 일괄 적용하는 것이 맞을 듯 하다. (ARM 컴파일러의 매뉴얼에 보면 unsigned를 default로 하는 이유가 나와 있다)

Posted by 안영기

2009/02/06 00:08 2009/02/06 00:08
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/4

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

printf()와 std::cout

지금도 그런지는 모르겠지만 가정 처음 C를 접할 때 나오는 함수는 “Hello world”를 출력하기 위한 printf()이고, C++ 책에는 같은 기능을 보여 주기 위해 std::cout였다. C++를 배우려고 하는 사람들이 이전의 C와 가장 이질감을 가지는 부분이 이곳인데 시작하면서부터 C C++은 문자 하나 출력하는데도 이렇게 다른 것이구나 라고 생각하게 된다

그런데 나와 나의 주위를 보자면, C++을 사용하는 현업에서도 printf()를 썼으면 썼지 std::cout은 거의 사용하는 사람이 없다. iostream의 사용법이 C++ operator 재정의의 사용 철학을 알리기 위해서 지금처럼 ‘<<’‘>>’를 사용하게 되었다고 들은 적이 있다. 하지만 printf()의 다양한 format을 표현하기 위해서는 알아야 하는 함수(또는 template, 또는 functor)가 너무도 많다. 진수(radix)를 바꾸기 위한 방법도 그렇고 출력 시 고정폭을 지원하게 하는 방법도 너무 귀찮다. (물론 처음부터 C++로 시작한 사람은 그냥 그려러니 하고 사용할 것이다. 도리어 C format함수가 더 어려울지도 모르겠다

내가 가장 큰 문제라고 생각하는 것은 printf() 보다 직관적이지 못한 부분이 있기 때문이다.예를 들어 아래의 두 가지는 서로 결과가 다르다. 그냥 보면 둘 다 같은 결과가 나올 것 같은데 그렇지 못하다. std::cout에서 결과가 예상한 것과 다르게 나오는 것은 operator overloading 때문이다.

소스
#include <stdio.h>
#include <iostream>

int main()
{
char ch = '0';

// 1. printf() 버전
printf("%c\n", ch+0);
printf("%c\n", ch+1);
printf("%c\n", ch+2);

// 2. std::cout 버전
std::cout << ch+0 << std::endl;
std::cout << ch+1 << std::endl;
std::cout << ch+2 << std::endl;

return 0;
}

결과

0  <-- printf() 버전
1
2
48  <-- std::cout 버전
49
50



Posted by 안영기

2009/02/01 08:54 2009/02/01 08:54
Response
0 Trackbacks , 3 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/5

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

해상도를 극복하자 (1/2)

1. 정보 전달의 요소

 

게임뿐만 아니라 디지털 기기를 통한 SW가 하는 일 중에 하나는 ‘정보의 전달’이다정보의 전달은 눈촉각 등에 의해 이루어지고 그것을 현실화 시키기 위해서 그래픽을 출력하고 사운드를 출력하며 진동을 발생시킨다그것들 중에 내가 그 동안 해왔던 부분은 그래픽을 통해 정보를 전달하는 부분이다비록 내가 게임 업계에 종사하고 있지 않아서 그 맥락을 잘 못 짚을 수는 있겠지만적어도 내가 해왔던 일 안에서는 그래픽을 통한 정보 전달이라고 하면 ‘이미지’와 ‘글자’를 통한 전달이 대부분이다.

 

‘이미지’에서는 2D, 3D로 나눌 수가 있으며 평면적인 그림 형태나 추상적인 아이콘의 형태나 3D 모델을 이루는 텍스쳐도 모두 이 ‘이미지’라는 것으로 아우를 수 있을 것이다또한 ‘글자’라고 하면 문자 출력을 통해 정보를 좀 더 구체화시키기 위한 여러 방법들까지 포함될 수 있을 것이다. (게슈탈트 붕괴를 시키면 글자도 이미지이므로 결국 모든 것은 이미지라고 주장할 수도 있겠다)

 

그럼 정보를 전달하기 위해서는 이미지와 글자가 출력되어야 하는데여기에 담긴 정보의 크기가 크면 클수록 표현의 품질은 더 높아진다고 할 수 있다하지만 정보의 크기를 크게 한다고 하여도 그것을 표현하기 위한 장비가 그 정보를 모두 수용하지 못하면 그것은 무용지물이 된다예를 들어 640x480인 동영상을 176x220인 휴대폰에서 재생하려면 많은 정보를 고의적으로 제거해야지만 제대로 화면에 출력을 할 수 있을 것이다결국 이 동영상을 처음부터 176x132로 인코딩한 것과 이론적으로는 동일한 품질 밖에 내지 못한다방금 예를 든 휴대폰에서의 문제의 원인은 무엇인가그것은 바로 소형 휴대 기기의 표현의 한계다시 말하면 해상도의 문제인 것이다.

 

이렇듯 기기에는 많은 표현의 제약이 있고우리는 여기에서 최대한 많은 정보(위에서 말한 ‘이미지’와 ‘문자’)를 나타내어 주기 위해 머리를 싸매고 있다본 강좌에서는 이런 고민을 하는 사람들을 위해 해결책 중에 한가지를 소개하려 한다.

 

 

 

 

2. 원자 구조와 픽셀 구조?

 

해상도가 낮은 기기에서 더 많은 표현을 하려면 어떤 방법이 있을까그것을 생각하기 위해서는 몇 가지 기본적인 구조에 대한 이해가 필요하다.

 

어릴 때 보던 과학 책에서는 원자 구조는 양성자+중성자와 전자로 이루어져 있다고 배웠다그런데 대학교에 들어가니 이것을 더 쪼갠 쿼크라는 존재를 알려주어 물질의 기본은 원자로부터’라는 종래 가치관을 근본부터 무너뜨렸다그럼 이것을 그래픽스에 적용해보자래스터 그래픽의 최종 단위는 픽셀이다’라는 절대 명제는 그래픽스 이론 서적에서도 제일 앞장에 나오는 말이다최근 서적에서는 잘 모르겠지만하여간 음극선관의 전자총의 원리와 함께 픽셀의 컬러가 조합되는 내용은 이 분야의 가장 기초적인 지식이었다그럼 앞서 말한 원자와 쿼크의 관계를 픽셀에도 적용해 보는 것이 이번 장의 주제이다..

 

불과 30년 전만 해도 우리는 흑백 TV를 보았었다그러다가 모 회사에서 ‘이코노 칼라 테레비젼’라는 히트작을 만들어 내면서 비로소 일반 서민(?)들도 컬러로 보내지는 방송을 볼 수 있게 되었다그 전까지는 ‘총천연색’이란 용어가 마케팅에도 쓰일 만큼컬러 화면이라는 것은 흔한 볼거리는 아니었다그런 이코노 테레비가 우리 집에도 한 대 들어 오게 되었고 14인치인데도 불구하고 아주 멀리 떨어져서만 보아야 하는 귀한 물건이 되었다.부모님의 잔소리에도 불구하고 매우 가까이서 TV를 보던 중 이상한 것을 하나 발견했다브라운관에 우연히 물방울이 하나 떨어지게 되었고 그것이 돋보기 역할을 하여 문제의 ‘픽셀’이란 것의 정체를 알게 된 것이다그때 기억하는 그의 모습은 다음과 같았다

 

사용자 삽입 이미지

단순한 유리라고만 알았던 것이 자세히 보면 벌집 같은(그때는 그렇게 느꼈다)것이 모여 있고 그 벌집 모양 하나 하나는 다시 빨강녹색파랑으로 이루어져 있었다좀 더 자세히 관찰해보니 그 3개의 불빛이 밝아졌다 어두워졌다 하는 것을 통해 한 색으로 병치 혼합되었고 그것들이 모두 모여서 하나의 화면을 구성했다당시에는 큰 발견이라 생각했지만 어른들은 그다지 관심을 가져주지 않았다.

 

그럼 다시 원자와 쿼크의 관계처럼 픽셀에 대해서도 정리해보자그러면 아마도 ‘하나의 픽셀은 빛의 삼원색을 나타내는 더 작은 요소로 되어 있고그 요소들의 가산 혼합에 의해 하나의 픽셀의 색을 결정하게 된다’라고 내용을 정리할 수 있을 것이다그렇다면 문제는 그 요소들이 어떤 식으로 배열이 되어 있냐는 것인데… 그것이 이 강좌의 핵심적인 부분을 이해하는데 필요한 내용이다.

 

 

3. 픽셀 구조의 이해

 

모든 강좌에서 남을 가장 쉽게 이해시키는 방법은 직접 눈으로 보여주는 것이다내가 이야기 하고자 하는 것을 쉽게 이해 시키기 위해 사진 하나를 첨부했다. (NDS 화면이다)

 

<사진>

 

왼쪽에는 보라색 선이 세로로 2개가 있고오른쪽에는 보라색 선이 세로로 3개가 있다그런데 여기서 왼쪽이 일반적인 보라색을 출력한 것이고 오른쪽의 것은 실제로 세로 2픽셀의 영역에 3개의 선을 보이게 한 것이다.정확하게 이야기 하면 중간의 선은 실제로 어느 픽셀의 소속이라고도 말하기 어려운 상태이다그러면 어떻게 이렇게 되는지 다음 그림을 보면 이해가 될 것이다.


사용자 삽입 이미지사용자 삽입 이미지

왼쪽의 그림은 픽셀 구조를 확대해서 그린 것이고 오른 쪽의 그림은 설명을 위해 약간의 부가적인 정보를 추가했다.

 

우리는 빨간색과 파란색을 섞으면 보라색이 된다는 것을 알고 있다그래서 일반적인 프로그래밍으로 보라색을 표시하면 왼쪽의 선과 같이 출력이 된다하지만 빛의 합성 원리 입각해서 보면 (1)도 보라색으로 보이지만(2)도 보라색으로 보여야 한다오히려 보라색의 경우에는 이렇게 어중간한 픽셀에 만든 선이 더 보라색에 가깝다실제 병치 혼합되는 거리가 가깝기 때문이다. (눈으로 직접 봐도 같은 결과다) (2)의 부분은 실제 8번 픽셀과 9번 픽셀의 경계를 걸쳐 형성되었는데 이렇게 만들려면 8번 픽셀은 파란색, 9번 픽셀은 빨간색으로 출력해야 한다.

 

방금 보라색의 예를 들어 보았다그렇다면 흰색의 경우는 어떨까흰색은 빨간색녹색파란색이 모두 가장 밝은 상태가 되면 만들어진다위의 보라색의 예와 같이 픽셀의 경계를 무너뜨리게 되면 다음과 같은 조합도 모두 흰색이 된다.

 

사용자 삽입 이미지

(1)은 평범하게 3x3의 흰색을 출력했을 때의 결과다그리고 (2)는 청록색-흰색-흰색-빨간색의 4x3을 출력한 것이고마지막 (3)은 파란색-흰색-흰색-노란색의 4x3을 출력한 것이다그런데 이 3개의 사각형은 모두 흰색의3x3으로 보인다좀 더 자세히 설명하자면, (1) RGB-RGB-RGB 3개의 점을 나타내고, (2) GBR-GBR-GBR  3개의 점을 나타내고, (3) BRG-BRG-BRG 3개의 점을 나타내기 때문에 사람의 눈으로는 모두 흰색으로 보이는 것이다단지 이 3개의 다른 점이라면 (2) (3)은 정확하게 픽셀에 딱 맞게 떨어지지는 않는다는 정도인데 이러한 편법을 적당히 이용하여 실제 해상도의 한계보다 더 많은 표현 하게 하자는 것이 이 강좌의 핵심이다.

 

그렇다면 이번에는 이 편법의 극적인 효과를 확인하기 위해 y = 3x의 그래프를 한 번 그려보자.

 

사용자 삽입 이미지

왼쪽은 이전 방식으로 프로그래밍했을 때이고오른쪽은 픽셀의 경계를 넘어가면서 그렸을 때의 결과이다화면이 마치 가로로 3배의 해상도를 더 가진 것처럼 보인다. DPI(dots per inch)가 큰 경우는 이상하게 보일지는모르겠지만 DPI가 작은 소형 게임기나 휴대폰 등에서는 마치 그 사이에 픽셀이 더 있는 것처럼 보인다. (Bresenham line 그리기 알고리즘도 이 편법으로 개선 시킬 수 있다.)

 

이제 사전 지식도 완벽하고 이 방식을 사용했을 때의 이점도 명확해졌다그렇다면 이것을 어디에 적용할 수 있는지를 생각해볼 차례다물론 그래픽 디자이너에게 이 방법을 알려줘서 256*192 해상도에서도 더 나은 퀄리티를 내게 할 수 있을는지도 모르겠다하지만 이 방법은 단점도 매우 많기 때문에 생각만큼 많은 곳에 응용되지는 못한다.

 

내가 생각한 최고 응용 법은이것을 통해 anti-aliasing 처리를 많이 줄일 수 있을 것이란 생각이다사실 anti-aliasing은 품질을 좋게 하기 위한 방법인데 왜 그걸 줄이느냐고 반문할 사람이 있을 수도 있겠다하지만 anti-aliasing은 ‘좋지 않게 된 품질’(alias가 있는)을 좋은 것처럼 보이게 하는 눈속임일 뿐이다그래서 가장 좋은 방법은 처음부터 alias를 만들지 않는 것이다.

 

그러면 역시 그림을 통해 그 결과를 확인해 보자.

 

사용자 삽입 이미지

왼쪽의 선은 y = 3x의 그래프에 anti-aliasing을 집어 넣은 것이고 오른쪽의 선은 픽셀 간 경계를 넘어선 방식이다. (모니터를 멀리하고 떨어져서 보면 두 선의 차이가 구분이 될 것이다)

간혹 그렇게 보았더니왼쪽의 선은 무채색으로 보이는데 오른 쪽은 그렇지 않다라고 생각하는 분도 있을 것이다하지만 제대로 본 것이다위처럼 픽셀을 크게 만들어 놓으면 녹색이 강조되기 때문에 선이 녹색 기준으로 보일 것이다이것은 인간의 시신경의 추상체가 RGB의 휘도를 동일하게 감지 하지 않는 것에 그 이유가 있고그것까지 고려해서 오른쪽 선을 그려야만 진정한 편법이 완성 되는 것이다.

 

 

4. 제약 사항

 

지금까지는 응용 가능한 부분에 대해서 이야기를 했지만 이번에는 제약 사항에 대해서 알아 보자.

 

-       LCD가 아니면 효과가 좋지 않다. PDP 판넬의 구조를 보면 LCD와 좀 다르다. (제조사마다 다르지만)

-       자신이 적용하고자 하는 디바이스의 LCD 구조에 맞게 구현이 수정되어야 한다. (NDS용이라면 좀 달라져야 한다)

-       검은 바탕에 흰 무엇 또는 흰 바탕에 검은 무엇을 표현할 때 효과가 크다.

 

소형 디바이스라면 대부분 LCD이겠지만 LCD pixel 구성 요소의 배열 구조는 제조사마다 다르다그러므로 그에 맞게 구현이 달라져야 하며설령 배열 구조는 같더라도 pixel pixel 사이의 간격이 큰 LCD라면 역시 적용이 불가능 하다또한 CRT나 일부 PDP의 경우에는 각 픽셀이 사각형의 격자 형식으로 빼곡히 들어가 있는 것이 아니라 interlace 단위로 서로 엇갈려 있는데이 경우도 적용이 불가능 하거나 적용 방식이 달라져야 한다(제일 처음 언급한 이코노 칼라 텔레비전의 구조를 생각하면 된다)

 

이 강좌의 처음에서정보 전달의 요소는 ‘이미지’와 ‘글자’라고 했다그리고 우리의 최종 목표는 이 ‘이미지’와 ‘글자’에 대해제한된 자원에서 최대한 많은 정보를 전달 할 수 있게 하는 것이다그럼 이 요소들에서 어떤 부분을 개선할 수 있을까위의 강좌 내용만 갖고도 생각할 수 있는 것은,

 

‘이미지’

축소 scaling을 할 때 더 세부적인 위치 정확도까지 묘사할 수 있다.

스프라이트가 더 세부적인 단위로 움직일 수 있다.

 

‘글자’

           - Anti-aliasing 없으면서 alias 없는 출력을 할 수 있다.

           더 작은 해상도에서도 가독성 있는 글자를 출력할 수 있다.

 

예로서 2가지씩만 들어 보았는데나머지는 당신의 창조적인 아이디어로 채워보자.

 

Posted by 안영기

2009/01/15 06:20 2009/01/15 06:20
Response
0 Trackbacks , 9 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/3

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

개발 철학

개발 철학란을 따로 분류를 했다.

 


2000년 전까지는 주로 완성품을 만드는 개발을 했다면, 2000년 이후부터는 남들이 완성품을 만들 수 있게 하기 위한 API를 만드는 일을 주로 했다.

 

API를 만들고 구현하는 일은 application을 만드는 일과는 사뭇 다르다. Use case에 따른 API 사용법을 디자인하는 것부터 시작해서, 사용자로 하여금 어떤 식의 프로그래밍을 유도할 것인지도 생각해야 하고, API 이름을 짓는 것부터도 어떤 형태의 일관성을 제공해야 한다. 그렇게 API 스펙과 구조 설계가 나오게 되면 API 구현에 대한 상서 설계가 나오게 되고, 그것을 기반으로 해서 기능 구현, 예외 보장, 속도 최적화와 같은 일을 하게 된다.

 

이런 일을 함에 있어서 필요한 것이 철학이다. 모든 철학은 그에 맞는 의미가 있기 때문에 서로의 우위를 가리기는 어렵다. (특히 높이 올라 갈수록 더 그렇다) 여기서 중요한 것은 그런 철학의 더 좋고 나쁘고를 따지는 것이 아니라 자신에게 철학이라고 말할 만한 것이 존재하는가 그렇지 않은가 하는 것이다. 나의 경우에는 신입 사원들을 개발 능력을 평가할 때, ‘철학’의 유무를 가장 먼저 파악한다. 요새는 인터넷으로 쉽게 좋은 글들을 발견할 수 있기 때문에 좋은 개방 방법론을 접하는 것이 쉬워졌다. 하지만 그런 방법론을 제대로 자기 것으로 만들지 못하면 결국은 흉내만 내는 것이며 머리 속에 정리된 자신만의 철학은 만들지 못한 것이다.

 

시대가 변하면서 나의 개발 철학도 조금씩 변해갔다. 점점 다듬어져 가는 것인지, 아니면 외골수로 가는 것인지는 알 수 없다. 내가 필요한 것은 나의 철학을 이야기 하면서 여러 사람들과 의견을 교환하는 것이다. 비난이 아닌 비평이 필요한 것이다

Posted by 안영기

2009/01/09 12:47 2009/01/09 12:47
Response
0 Trackbacks , 2 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/2

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

C의 변방에 있는 문법

다음의 코드를 VC++에 넣고 돌려보면 아무런 문제가 없이 빌드된다.
두 번째의 int a 에서 중복 선언 에러가 날 법한데도 말이다.

int main()
{
int a = 100;
// 'a' : redefinition ???????????/
int a = 0;

return a;
}

왜일까... 그래서 gcc에서 돌려 보면 
warning: trigraph ??/ ignored, use -trigraphs to enable
라는 경고를 준 후 정상적으로 에러를 낸다.

혹시나 해서 위의 경고 대로
gcc a.c -trigraphs
이렇게 컴파일을 하면 VC++에서처럼 문제없이 빌드가 된다.

이렇게 되는 이유는 C 언어의 trigraph라는 문법 때문이다. 원래는 C 언어에서 사용하는 모든 캐릭터에 대해, 모든 키보드가 그 키를 가지고 있는 것이 아니었기 때문에 만들어진 것인데 물음표 2개 뒤에 특정한 캐릭터를 더 붙이면 그것이 1개의 다른 캐릭터로 치환되는 것이다. 위의 '??/''\'로 치환이 된다.

공교롭게도 '?' 와 '/' 는 같은 키이다. '?'를 연속으로 출력하고자 하다가 실수로 shift 키를 먼저 떼는 실수를 했다면 저렇게 '??/'라는 문자열로 끝날 때가 종종 있다. 이렇게 되면 이것은 마지막이 '\'로 해석이 되니 다음 줄은 주석과 같은 줄로 취급하게 되어 그 줄도 주석이 된다.

이런 실수를 하게 되었을 때 이 문법 자체를 모른다면 해결하기가 쉽지는 않을 것이다.

Posted by 안영기

2009/01/09 09:13 2009/01/09 09:13
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/1

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

« Previous : 1 : 2 : 3 : 4 : 5 : Next »

블로그 이미지

게임 개발을 기반으로 한, 잡다한 개발 기록 저장소

- 안영기

Notices

Archives

Authors

  1. 안영기

Recent Comments

Recent Trackbacks

Calendar

«   2024/03   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Site Stats

Total hits:
235623
Today:
84
Yesterday:
38