1학기 결산-게임엔진프로그래밍활용

이미지
1. 브레젠험 직선 알고리즘 컴퓨터에서 계산이 느린 실수 연산을 사용하지 않고 직선을 그리기 위해 만들이진 알고리즘입니다. 평면을 아래와 같이 8분 면으로 나누어 직선을 그립니다. 링크:  https://sulinep.blogspot.com/2020/05/bresenhams-line-algorithm.html 2. 엔진 기초 수학 여기서는 본격적으로 구현에 들어가기 전 기초 수학 지식을 쌓았습니다.  수학에서의 체는 대수적 구조의 하나로 덧셈, 뺄셈, 곱셈, 나눗셈의 사칙연산을 집합 안에서 소화할 수 있는 집합을 의미합니다. 체를 이루기 위한 조건과 체라는 개념을 알아보았습니다. 처음에 체 라는 개념이 뭔가 머릿속에서 애매했는데 이후 갈로이스 체에 대해 배운 후 조금 더 명확해졌습니다. 스칼라 는 벡터를 정의하기 위한 필수 요소이고 크기만 있고 방향이 없는 성분이다. 벡터는 크기와 방향을 포함하는 표현 도구이다. 겨기서 벡터의 기본 연산자들을 알아보았습니다. 선형성 은 직선처럼 똑바른 도형 또는 그와 비슷한 성질을 가진 대상이라는 뜻으로 함수의 경우 함수가 진행하는 모양이 직선이라는 의미로 사용된다. 선형성을 만족하려면 두 가지 조건을 만족해야 하는데 균질성과 첨가성이다. homogeneity (균질성):   additivity (첨가성): 선형이라고 부르는 수식들은 중첩의 원리 가 적용된다는 특징이 있다. 이때 행렬과 선형 변환의 관계에 대하여도 알아보았었는데 선형 변환과 행렬은 1:1 대응된다. 기저 란 어떤 벡터 공간을 선형 생성하는 선형 독립인 벡터들이다. 각각의 원소들이 다시 벡터 공간을 생성할 수 있어야하고 일차 독립이어야 한다. 표준 기저 는 많은 기저들 중 성분 1개만이 1이고 나머지 성분이 모두 0인 표준 적인 벡터이다. 여기서 벡터 공간 R의 기저를 구성하는 원소의 개수가 해당 공간의 차원 이다. 행렬 은 열기반 행렬과 행기반 행렬 중 어떤 걸 사용하느냐에 따라 계산 방식이 달라진다. 여기서는 벡터의 크기, 회전, 밀기, 선형 변환에 대하여

[알고리즘] 브레젠험 직선 알고리즘 (Bresenham's line algorithm)

이번에는 과제로 나온 브레젠험 직선 알고리즘에 대한 글입니다.

1. 브레젠험 직선 알고리즘이란?
컴퓨터에서 계산이 느린 실수 연산을 사용하지 않고 직선을 그리기 위해
브레젠험(Bresenham's) 이라는 분이 만든 알고리즘입니다.

2. 기본 원리
일단 선을 만들려면 두 점이 필요합니다.
 
의 좌표로 이루어진 두 점이 있다고 보면 점에 대한 직선의 방정식은

     으로 정의할 수 있습니다.  좀더 단순하게 바꾸면

    으로도 정의할 수 있습니다.

위와 같이 무언가 수식을 정리하긴 했습니다.
이제 저걸 어떻게 사용하는지가 핵심이라고 봅니다.

기본적으로 스크린 좌표는 정수형입니다.
브레젠험 알고리즘은 평면을 아래와 같이 8분 면으로 나누어 그립니다.
저렇게 나누어진 분면 위에서 점이 한 칸 더 가서 그려지냐 아니면 그대로 그려지냐 판별하는 것이 핵심입니다.


3. 1분면에 대해 선 그리기

브레젠험 알고리즘은 기울기가 0과 1사이일 때를 가정해 정리가 됩니다.

1분 면에서 아래와 p1과 p2를 이어주는 선을 그린다고 가정을 해봅시다.

사각형 하나는 1픽셀입니다.
그림을 보면 그리려 하는 선이 초록색으로 강조해놓은 팩셀 모두를 지나갑니다.
이때 위의 픽셀과 아래의 픽셀 중 하나를 선택해 그려줘야 합니다.

여기서 기울기는 항상 0과 1사이라서 x 값은 항상 1씩 증가합니다.
y 축은 L1 보다 L2의 값이  작으면 y값을 1 증가시켜줍니다.


위의 사이 값을 아래의 좌표와 같다고 생각해봅시다.


위의 좌표 값을 대입한 값이 해당 지점의 값  을 대입했을 때의 실제 Y값 보다 작으면 해당점은 픽셀위치의 변동 없이 그려주고 반대의 경우에는 한칸 아래에 그려줍니다.
해당 지점의 값을 직선의 방정식에 대입해보겠습니다.



그리고 y의 값은  에서 0.5 증가했으니 아래와 같은 판별식을 만들수 있습니다.



하지만 나눗셈이라던지 0.5 실수를 더한다던지 아직 속도가 비교적 느린 부족한 부분이 있습니다.
양변에 w를 곱하고 조금더 간소화 시켜보겠습니다.



아직 실수 연산이 남아있네요. 그럼 변에 2를 곱해서 마무리를 짓겠습니다.



간단한 판별식이 나왔습니다.




 두번째 지점에 대한 판별식을 보겠습니다.
첫번째 점의 판별식  봅시다.
일정한 패턴이 있는 것을 확인 하수 있습니다.

이로서 모든 준비가 되었습니다.

이제 두 번째 픽셀의 y값을 정하기 위해 점화식 을 구합니다.
해당 지점의 판별식을 통해 y 값이 그대로 유지되면 다음 판별식은 2h를 더하고
y 값이 증가하면 다음 판별식은 2h-2w를 더한 값이 된다.
x의 값이 목표에 도달할 때까지 위의 과정을 반복하면 된다.

이로서 1분면에 선을 그릴수 있게 되었습니다!

int y = startPosition.Y;
int det = (2 * height) - width; // 점화식
for (int x = startPosition.X; x != endPosition.X; x += 1)
{
 if (det < 0) // 
 {
  det += 2 * height;
 }
 else
 {
  y += 1;
  det += (2 * height - 2 * width);
 }
 SetPixel(ScreenPoint(x, y), InColor);
}

4. 나머지 분면
일단 2팔분면을 생각해보겠습니다.


1팔분면에서는 x의 값을 증가하고 y의 값을 판별했습니다.
하지만 2팔분면에서는 x의 값을 1식 증가시킬 수 없기 때문에
반대로 생각해야 합니다. 판별식은 같습니다. 단지 y를 증가하고 x의 위치를 판별하면 됩니다.

판별식은  가 될 겁니다.


하나 더 생각해보겠습니다. 8분면을 생각하면 y의 값이 점점 줄어 들어야합니다.

나머지 분면도 이런식으로 생각해서 알고리즘을 사용하면 모든 분면에 선을 그릴수 있습니다.

5. 최종

// 넓이와 높이는 절대값으로 구해둠
int width = abs(endPosition.X - startPosition.X);
int height = abs(endPosition.Y - startPosition.Y);
int Yfactor = endPosition.Y < startPosition.Y ? -1 : 1;
int Xfactor = endPosition.X < startPosition.X ? -1 : 1;

// 넓이가 높이보다 큰경우는 1,4,5,8 분면
if (width > height)
{
 int y = startPosition.Y;
 int det = (2 * height) - width; // 점화식
 for (int x = startPosition.X; x != endPosition.X; x += Xfactor)
 {
  if (det < 0) //판별
  {
   det += 2 * height;
  }
  else 
  {
   y += Yfactor;
   det += (2 * height - 2 * width);
  }
  SetPixel(ScreenPoint(x, y), InColor); // 점을 그려줌
 }
}
else
{
 int x = startPosition.X;
 int det2 = (2 * width) - height; // 점화식
 for (int y = startPosition.Y; y != endPosition.Y; y+= Yfactor)
 {
  if (det2 < 0)
  {
   det2 += 2 * width;
  }
  else
  {
   x += Xfactor;
   det2 += (2 * width - 2 * height);
  }
  SetPixel(ScreenPoint(x, y), InColor);
 }
}

















댓글

이 블로그의 인기 게시물

[알고리즘] 코헨 서더랜드 알고리즘(Cohen–Sutherland algorithm)

[게임 수학] 외적을 이용한 왼쪽 오른쪽 구분

오일러 각, 로드리게스 회전 공식, 평면의 방정식