화면상 “한열(column)당 한가지의 정보”만 있으면 됩니다. 정확하게는 “열(column) x 타일사이즈(TILE_SIZE)당 한가지의 정보”만 있으면 3D화면으로 확장시킬 수 있습니다.
그 정보들은 이미 2D미니맵을 구현하면서 설정되었고 그 정보들을 이용해서 구현만 하면됩니다.
2️⃣ 3D랜더링 순서 정하기
(1) 단순하게 생각할 수 있는 순서 및 방법
이전에 구현한 “player와 광선(ray)“는 미니맵이 랜더링된 후에 덧칠하는 느낌으로 그려지도록 했습니다.
이러한 덧칠하는 방법으로 구현하려고하면 3D랜더링이 가장 먼저 실행되어야합니다. 하지만 3D랜더링을 하기 위해서는 광선(ray)을 구현하면서 설정해줬던 정보들이 필요합니다. 그렇기 때문에 지금 생각할 수 있는 방법은 다음과 같이 2가지 입니다.
“2D미니맵랜더링” -> “3D화면랜더링” -> “2D미니맵랜더링” 으로 마지막에 2D미니맵을 한번 더 덧칠해주는 방법
“3D화면”을 기준으로 먼저 함수를 구현하고 그정보를 이용한 “2D미니맵랜더링”함수 구현하기
1번의 방법은 가장 단순하게 해결할 수 있지만 성능상 가장 비효율적인 방법입니다.
2번의 방법이 괜찮을 것 같지만 추가적으로 각각의 광선(ray)의 정보를 담고 있는 ray구조체 배열을 만들어 줘야합니다. 어쩔수 없이 광선(ray)을 하나씩 살피는 반복문도 필요합니다. (3D화면또한 한 열씩 그려지는데 그 열의 위치가 2D맵이 그리는 열과 반드시 같지 않기 때문에 덮여 씌어지는일이 생길 수 있고 어쩔 수없이 배열과 추가적인 반복문이 필요합니다.) 결론적으로 코드를 고치는 것이 불편할 뿐더러 효율도 그다지 좋아보이지 않습니다.
(2) 이상적인 방법
이 방법이 이상적인 방법이라고는 확신하지 못하지만 그래도 위의 방법보다는 이상적인 방법이라고 생각합니다.
기존에 광선을 동시에 2개씩그려주는 반복문을 왼쪽부터 하나씩그려주는 반복문으로 수정해 주었습니다.
3D랜더링함수가 draw_one_ray함수에 위치할 계획입니다.
(5) 3D랜더링함수 최종위치
voiddraw_one_ray(t_god*god,doubleangle,inti){/* 코드 생략 */draw_line(god,god->ray.wall_hitX-god->player.x,god->ray.wall_hitY-god->player.y);render_3D_project_walls(god,i);}
각각의 광선(ray)에 대한 정보는 draw_one_ray함수에 와서야 결정이 됩니다. 그렇기 때문에 3D랜더링함수의 위치를 이 함수의 끝쪽으로 최종결정하였습니다.
draw_line함수(광선(ray)을 그리는 함수)와 render_3D_project_walls함수의 위치는 서로 바뀌어도 상관없습니다.
god->ray.wasHit_vertical ? 0xFFFFFF : 0xAAAAAA;코드를 이용하여 수직면과 수평면의 색을 다르게 하였습니다.
if (god->img.data[ ... ] == 0x111111)의 조건문을 이용하여 현재위치의 픽셀의 색이 초기에 설정해준 색이면 색을 덧입히도록 구현하였습니다.
FOV_ANGEL은 시야각을 나타내며 60 * (PI / 180.0)으로 정의하였습니다.
distance_project_plane은 화면(모니터)과 사용자사이의 거리입니다. WINDOW_WIDTH와 FOV_ANGEL을 알면 간단하게 구할 수 있습니다.
wallStripHeight는 화면(모니터)상에 보이는 벽의 높이입니다. 거리(god->ray.distance)와 반비례하며 distance_project_plane을 곱하여 적절한 높이를 가지게 만들었습니다. (실제 거리가 1일때 높이와 2 : 루트3비율을 갖게 되어 적당한 비율이라고 생각)
4️⃣ 문제점 발견 및 수정
(1) 왼쪽, 위쪽 출력문제
< 왼쪽방향을 바라볼때 출력 >
< 위쪽방향 바라볼때 출력 >
왼쪽방향으로 바라볼 때 벽의 위치가 한칸 밀려서 생성됨을 확인할 수 있었고 위쪽방향으로 바라볼 때는 미세하지만 약간 뒤로 밀려서 출력됨을 확인할 수 있습니다.
바라보는 방향에 따라 보정을 잘못해준 것이 원인이였습니다.
< 수정 전 >
voidcal_ray(t_god*god,t_dpable_ray*hv){/* 코드 생략 */while(next_touchX>=0&&next_touchX<=WINDOW_WIDTH&&next_touchY>=0&&next_touchY<=WINDOW_HEIGHT){if(is_wall(next_touchX,next_touchY-(god->ray.isRay_facingUp?1:0))){/* 코드 생략
}
/* 코드 생략 */}
< 수정 후 >
voidcal_ray(t_god*god,t_dpable_ray*hv,inta,intb){/* 코드 생략 */while(next_touchX>=0&&next_touchX<=WINDOW_WIDTH&&next_touchY>=0&&next_touchY<=WINDOW_HEIGHT){if(is_wall(next_touchX+a,next_touchY+b)){/* 코드 생략 */}}/* 코드 생략 */}
horizontal, vertical의 각각의 수치를 계산하는 함수의 공통된 부분을 묶어만든 함수에서 바라보는 방향에 따른 보정을 horizontal일 경우로 맞췄었습니다.
수정 후 cal_ray함수는 a(x좌표 보정값), b(y좌표 보정값)을 매개변수로 받아서 적용시킬 수 있도록 변경하였습니다.
(2) 면이 굴곡이 생기는 문제
예전에 자바스크립트로 구현했을때도 언급했던 문제입니다. (어안렌즈효과?)
지금까지 레이케스팅이 사용자(꼭지점)기준으로 60도의 시야각을 가지고 바라봤을때의 물체가 출력되도록 구현했습니다.
그렇기 때문에 같은 모니터화면이라도 모니터안에서의 좌우의 위치에 따라서 거리가 달랐습니다. (당연히 부채꼴 모양으로 광선이 나가므로)
하지만 이렇게 보이는 물체의 모습은 우리에게 익숙하지 않습니다. (사람은 두개의 눈으로 들어온 각각의 빛을 뇌에서 보정(?))
이것을 같은 모니터상이라면 좌우 어느지점이라도 가운데에서의 거리에서 볼때와 비율이 같아지도록 보정해주었습니다.