42Seoul/Cub3D

[Cub3D] 레이캐스팅(Ray casting)이란?

  • -
728x90

<참조> github.com/365kim/raycasting_tutorial/blob/master/2_basics.md

 

365kim/raycasting_tutorial

(한글ver) 레이캐스팅 튜토리얼 번역. Contribute to 365kim/raycasting_tutorial development by creating an account on GitHub.

github.com

<원문> lodev.org/cgtutor/raycasting.html

 

Raycasting

#define mapWidth 24 #define mapHeight 24 #define screenWidth 640 #define screenHeight 480 int worldMap[mapWidth][mapHeight]= { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,

lodev.org

레이캐스팅은 2차원 맵에서 3차원의 원근감을 만드는 렌더링 기술이다.

  • 레이캐스팅은 스크린의 모든 수직선에 대해 계산만 하면 돼서 속도가 빠르다
  • 예전의 컴퓨터는 현재보다 느려서 3D 엔진을 실시간으로 실행할 수 없던 과거에는 레이캐스팅이 최초의 해결책이었다.

레이캐스팅 기술을 사용한 게임 중 가장 유명한 게임은 Wolfenstein 3D이다.

 

레이캐스팅과 레이트레이싱은 다르다.

  • 레이캐스팅은 4MHz 그래픽 계산기에서도 실시간으로 작동하는 빠른 semin-3D 기술인 반면, 레이트레이싱은 실제 3D장면의 반사, 그림자를 지원해서 현실감있는 렌더링 기술이다.

기본적인 원리

  • 2차원 정사각형 그리드로 된 맵이 있다.
  • 맵의 한 칸은 0 또는 양수 값을 갖는다.
    • 0은 벽이 없음을 의미하고, 양수값은 특정 벽을 의미한다.
  • 화면의 모든 x값(수직선)에 대해 플레이어 위치에서부터 시작하는 광선(Ray)를 쏜다.
  • 이 때 광선의 방향은 플레이어가 바라보는 방향, 그리고 화면의 x좌표에 의존한다.
  • 이 광선은 2D맵 위에서 벽에 부딪힐 때까지 직진하다가, 벽에 부딪히면 적중지점(hit point)으로부터 플레이어까지 거리를 잰다.
  • 이 거리에 따라 벽의 높이가 화면에 얼마만큼 그려져야 하는지 결정된다. 벽의 높이는 플레이어와 벽 사이의 거리가 멀수록 화면에 더 낮게, 가까울 수록 더 높게 표시된다.
  • 이것들은 모두 2차원 계산으로 구할 수 있는 것들이다.
  • 상단의 이미지는 위에서 내려다 본 2차원 평면도 이다.
  • 광선이 플레이어에서 시작해서 벽에 도달하는 것을 보여준다.

광선이 처음으로 부딪히는 벽을 찾으려면, 광선을 플레이어의 위치에서부터 출발시켜 광선이 벽에 포함되는지 반복적으로 검사 해야한다.

  • 광선이 벽에 포함되는 것으로 확인되면, 벽에 포함되는지 확인하던 loop는 멈추게 되고, 거리를 측정해서 알맞은 높이로 벽을 표현해준다.
  • 반대로 광선이 벽에 포함되지 않는 것으로 확인되면, 계속해서 추적합니다. 광선 방향에 맞는 새로운 위치에서 벽에 포함되는지 다시 검사한다. 벽에 부딪힐 때까지 계속해서 반복한다.

광선이 어디서 벽에 부딪히는지 한눈에 알 수 있는 사람과는 다르게, 컴퓨터는 광선의 경로 상에 있는 한정된 지점들만 검사할 수 있기 때문에 공식 하나로 뚝딱 알아낼 수 없다.

레이캐스터는 보통 관선의 위치에 일정한 값을 더해주며 반복하는 방식으로 벽에 부딪혔는지 검사한다.

  • 이렇게하면 벽에 부딪혔는데도 이를 놓치고 벽에 부딪히지 않았다고 판단할 가능성도 있다.
  • 예를 들어 상단 이미지의 광선에서 일정한 간격으로 떨어져있는 적색 점들에서 검사한 경우를 보자.
  • 광선은 파란색 벽을 통과하여 직진하지만 컴퓨터는 빨간색 점이 있는 위치에서만 확인했기 때문에 이를 감지하지 못한다
  • 더 많은 위치를 확인하면 컴퓨터가 벽을 놓칠 가능성이 줄어들지만 더 많은 계산이 필요하다.

이번에는 검사지점의 간격이 반으로 줄여서 광선이 벽을 통과했음을 확인할 수 있게 된다. 그래도 위치가 정확하지는 않다는 문제점이 있다.

광선이 닿는 벽의 모든 면을 검사하는 방법이다.

  • 정사각형 한 칸 너비를 1이라 하면, 모든 벽면을 정수값으로 표현할 수 있다

 

 

이 튜토리얼에서는 DDA알고리즘을 기반으로 진행한다.

  • DDA 알고리즘은 2차원 그리드를 지나가는 선이 어떤 네모칸과 부딪히는지 찾을 때 일반적으로 사용되는, 속도가 빠른 알고리즘이다. 그래서 이 알고리즘을 사용해서 광선이 맵에서 어떤 네모칸이랑 부딪히는지 찾아낼 수 있고, 벽에 부딪힌 것이 확인되면 이 알고리즘은 중단된다.
  • 일부 레이트레이서는 유클리드 각도를 활용하여 플레이어의 방향과 광선을 나타내며 시야를 결정하지만 벡터와 카메라로 작업하는 것이 훨씬 쉽다.
  • 방향은 방향벡터의 x,y 두 값으로 결정된다.
    • 플레이어가 보는 방향으로 선을 그릴 경우, 그 선위의 모든 점들은 플레이어의 위치 + 방향벡터의 배수의 합이다.

  • 벡터를 이용하는 방법에는 방향벡터 외에 카메라평면도 필요하다.
    • 위 이미지에서 카메라 평면(파란색)은 컴퓨터 화면의 표면을 나타내고 방향벡터(검정)은 화면 내부 쪽을 가리킨다.
    • 카메라 평면은 항상 방향벡터에서 수직이다.
    • 점으로 표현되는 플레이어의 위치는 카메라 평면보다 앞에 있다.
    • 화면에서 특정 x좌표의 특정 광선은 이 플레이어 위치에서 시작하여 화면의 해당 위치 또는 카메라 평면을 통과하는 광선이다.
    • 진짜 3D 엔진은 3차원을 다루므로 벡터 2개가 필요하지만. 2차원 맵을 다루는 레이캐스팅은 카메라평면이 진짜 평면이 아니고 선이므로 벡터 1개로 표시한다.
  • 벡터의 덧셈을 이용하여 다음과 같이 필요한 벡터를 표현해보자.
    • pos 벡터 : 플레이어의 위치(green spot)
    • dir 벡터 : 방향벡터(black line)
    • plane 벡터 : 전체 카메라평면(blue line) 중 방향벡터의 끝점(black spot)부터 오른쪽 카메라평면의 끝점(blue spot)까지
    • 방향벡터 끝점(black spot) : pos + dir
    • 오른쪽 카메라평면의 끝점(right blue spot) : (pos + dir) + plane
    • 왼쪽 카메라평면의 끝점(left blue spot) : (pos + dir) - plane
  • 이제 광선의 방향 은 카메라평면으로부터 쉽게 구할 수 있습니다.
    • 계산방법은 ( 방향벡터 ) + ( 카메라평면 x 배수 ) 입니다.
    • 예를 들어 이미지에서 적색 선은 광선(Ray)를 나타내는데, 카메라평면의 오른쪽에서 길이의 약 1/3 지점을 통과하는 세 번째 광선을 보겠습니다.
    • 광선의 방향 : dir + plane * 1/3
    • 이 광선의 방향은 rayDir 벡터 라고 하고, 벡터의 X, Y값은 DDA 알고리즘에 사용됩니다.

  • 바깥쪽 선 두개는 스크린의 왼쪽/오른쪽 경계이고, 두 선 사이의 각도를 FOV (Field of View) 라고 한다.
    • FOV는 " 방향벡터 길이 : 평면 길이 " 의 비율로 결정된다.
    • 다른 FOV 의 몇 가지 예는 다음과 같다.
    • 1] 방향 벡터와 카메라 평면 벡터의 길이가 같은 경우, FOV는 90 °이다. (좌측 이미지)
      • 방향벡터 길이 : 평면 길이 = 1 : 1
    • 2] 방향 벡터가 카메라 평면보다 훨씬 길면 FOV가 90 °보다 훨씬 작아진다. (중앙 이미지)
      • 시야가 좁아져서 더 자세한 내용을 볼 수 있고 깊이가 줄어들므로 확대와 동일하다.
      • 방향벡터 길이 : 평면 길이 = LONG : 1
    • 3] 방향 벡터가 카메라 평면보다 짧으면 FOV가 90 °보다 커진다.(우측 이미지)
      • 방향 벡터가 0에 가까울 경우 180 °가 최대이다.
      • 축소와 같이 훨씬 넓은 시야를 갖게된다.
      • 방향벡터 길이 : 평면 길이 = 1 : LONG

 

728x90
300x250
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.