[3D게임 수학&물리] 원형 그라데이션 그리기

Herring·2021년 4월 14일
0
post-thumbnail

개인적으로 공부한 내용을 작성한 것으로 틀린 부분이 있을 수 있습니다.
혹 틀린 부분이 있다면 말씀해주시면 감사하겠습니다.🙏

1. 기본 원형 그라데이션

1) 원의 중점

먼저 위 이미지에서 표시되는 원의 중점은 화면의 중앙에 위치해 있다.
= 시작점이 x축으로 WIDTH/2, y축으로 HEIGHT/2 만큼 이동되어 있다.
= (WIDTH/2, HEIGHT/2) 부터 (0, 0)으로 시작해야 한다.
따라서 이를 기준점으로 한 좌표 (dx, dy)를 식으로 나타내면
dx = x - WIDTH/2, dy = y - HEIGHT/2 이다.

2) 원의 방정식 적용

위 이미지는 반지름과 밝기가 c인 수많은 원형의 선이 그려진 형태로 볼 수 있다.
피타고라스의 정리에 의해 원점으로부터 c(반지름)까지의 거리는 √(x^2 + y^2) 이고
(원의 방정식: x^2 + y^2 = c^2)
저번과 마찬가지로 값이 256을 넘어서면 색상값은 (0,0,255)가 되므로,
이를 코드로 작성하면 아래와 같다.

소스 코드

int RenderScanLine( void )
{
   int x, y;
   float dx, dy; // 화면 중심을 기준으로 한 좌표
   int nBright;

   for ( y = 0; y < VIEW_HEIGHT; y++ ) {		// y 방향 루프
      for ( x = 0; x < VIEW_WIDTH; x++ ) {	// x 방향 루프
         dx = x - VIEW_WIDTH/2.0f;		// 상대 x 좌표
         dy = y - VIEW_HEIGHT/2.0f;		// 상대 y 좌표
         // 밝기가 화면 중심으로부터의 거리에 연동
         nBright = (int)sqrtf( dx*dx + dy*dy ) & 0xff;
         DrawPoints( x, y, nBright, nBright, 255 );
      }
      FlushDrawingPictures();
   }
   return 0;
}

2. 일식(Eclipse) 스타일 그라데이션

위와 같은 일식 느낌의 그라데이션을 그린다고 생각해보자.

특징
1. 반복 없음: 그라데이션이 반복되지 않는다((0,0,0)을 유지).
2. 색상 변화: 색상이 cyan(0,255,255) -> black(0,0,0)으로 변화한다.
3. 일부만 그라데이션: 특정 지점(RAD1)까지 (0,0,0)이었다가 (0,255,255)->(0,0,0)이 된다.

1) 반복 없음

1번과 달리 그라데이션이 반복되지 않으므로 & 0xff 연산을 하지 않는다.


2) 색상 변화

1번의 이미지에서는 밝기가 x,y의 값에 비례하여 밝아지지만, 여기서는 그 반대이다.
따라서 nBright = 255 - nBright 이 되어야 하고, 가장 밝은 색상이 cyan이므로 최종 픽셀 값은 (0, nBright, nBright) 이다.


3) 일부만 그라데이션

이미지 상에서 중심의 까만 원의 반지름을 RAD1, 그리려는 반지름을 rc라고 하면
RAD1까지는 원이 그려지지 않아야 하므로 if문으로 조건을 걸어 rc > RAD1 이 되는 지점부터 그려지도록 해야 한다.
if (rc > RAD1) { nBright = (int)(255.0f - rc); } 이다. (*rc = √(x^2 + y^2))
여기까지를 출력해보면 다음과 같이 나타난다.

일식 느낌은 나지만 뭔가 좀 어둡다... 목표 이미지대로라면 rc = RAD1 이 되는 지점에서 최대 밝기여야 한다.
= rc = 0~RAD1일 때까지 가장 밝다가 이후 점점 어두워진다.
= rc = RAD1일 때 0, rc = RAD1+1일 때 1... 즉 rc를 RAD1만큼 빼줘야한다.
= nBright = (int)(255.0f - (rc-RAD1));
(사실 rc<RAD1일 때도 위의 식대로라면 256 이상의 값이 되어 최대밝기가 나오긴하지만 조건문에 의해 그려지지 않는다.)

이번엔 빛이 너무 멀리까지 퍼져나간다. 목표 이미지대로라면 특정 지점(RAD2)까지만 그라데이션이 그려져야 한다.
이번에도 if문으로 조건을 걸어 rc < RAD2 이 되는 지점까지만 그려지도록 해야 하지만,
단순히 if문으로 자르기만 해선 안 되고, 그라데이션의 밝기 비율도 조정해야 한다.
rc=RAD1에서 최대밝기, rc=RAD2에서 최소밝기가 되어야 한다.
= rc=RAD1일 때 0%, rc=RAD2일 때 100% 이다. (255에서 빼니까 퍼센티지는 밝기에 반비례)
= rc-RAD1=0일 때 0*255, rc-RAD1=RAD2-RAD1일 때 1*255 이다.

여기서 양변에 (RAD2-RAD1)을 나눠주면,
(rc-RAD1)/(RAD2-RAD1)에서 0이 되고 (rc-RAD1)/(RAD2-RAD1)에서 1이 되므로 여기에 255를 곱해주면 된다.
= nBright = (int)(255.0f - (rc-RAD1)/(RAD2-RAD1) * 255.0f);

소스 코드

int RenderScanLine( void )
{
   int x, y;
   float dx, dy; // 화면 중심을 기준으로 한 좌표
   int nBright;

   for ( y = 0; y < VIEW_HEIGHT; y++ ) {		// y 방향 루프
      for ( x = 0; x < VIEW_WIDTH; x++ ) {	// x 방향 루프
         dx = x - VIEW_WIDTH/2.0f;		// 상대 x 좌표
         dy = y - VIEW_HEIGHT/2.0f;		// 상대 y 좌표
         rc = sqrtf(dx * dx + dy * dy);
         if ((rc > RAD1) && (rc < RAD2)) {
             // 밝기가 화면 중심으로부터의 거리에 연동
             nBright = (int)(255.0f - (rc - RAD1) / (RAD2 - RAD1) * 255.0f);
             DrawPoints(x, y, 0, nBright, nBright);
         }
      }
      FlushDrawingPictures();
   }
   return 0;
}

3. 경계를 부드럽게 하기(Anti-aliasing)

이전 이미지에서는 그라데이션이 시작되는 경계부분이 매끄럽지 못하고 픽셀의 계단현상이 그대로 나타난다.
이를 부드럽게 보이도록 처리하는 것을 안티앨리어싱(Anti-aliasing)이라고 한다.
안티앨리어싱을 적용해보자.

경계가 부드럽다
= 밝기의 변화가 덜 급격하다
= 경계 부분에 아주 좁은 그라데이션 하나를 만들어주면 될 것이다.
즉 기존의 RAD2를 RAD3이라고 하고, (RAD1~RAD2) 구간과 (RAD2~RAD3) 구간으로 나누어 그린다.

따라서
cr>RAD1 && cr<RAD3 내에서 픽셀을 그리되
cr<RAD2 라면 안티앨리어싱 구간이므로 nBright = (rc-RAD1)/(RAD2-RAD1) * 255 이겠고
그렇지 않으면 원래 그라데이션 구간이므로 nBright = (255-(rc-RAD2)/(RAD3-RAD2) * 255 이다.

소스 코드

int RenderScanLine( void )
{
   int x, y;
   float dx, dy; // 화면 중심을 기준으로 한 좌표
   int nBright;

   for ( y = 0; y < VIEW_HEIGHT; y++ ) {		// y 방향 루프
      for ( x = 0; x < VIEW_WIDTH; x++ ) {	// x 방향 루프
         dx = x - VIEW_WIDTH/2.0f;		// 상대 x 좌표
         dy = y - VIEW_HEIGHT/2.0f;		// 상대 y 좌표
         rc = sqrtf(dx * dx + dy * dy);
         if ( ( rc > RAD1 ) && ( rc < RAD3 ) ) {
         // 밝기가 화면 중심으로부터의 거리에 연동
            if ( rc < RAD2 ) {
            nBright = ( int )( ( rc - RAD1 ) / ( RAD2 - RAD1 ) * 255.0f );
            }
            else {
               nBright = ( int )( 255.0f - ( rc - RAD2 ) / ( RAD3 - RAD2 ) * 255.0f );
            }
            DrawPoints( x, y, 0.0f, nBright, nBright );
         }
      }
      FlushDrawingPictures();
   }
   return 0;
}

실제 일반 3D그래픽에서는 폴리곤 테두리에 이렇게 일일이 그라데이션을 더하기 어려우므로 보통은 멀티 샘플링 안티앨리어싱(MSAA) 이라는 기법을 이용한다.

0개의 댓글