티스토리 뷰

벽 레이캐스팅, 스프라이트 카메라행렬에서 곧바로 이해 안됐던 부분만 기록용으로 정리

github.com/365kim/raycasting_tutorial

github.com/l-yohai/cub3d/blob/master/mlx_example/sprite.md


레이캐스팅 (wall)

a, b: 벡터
(a + b)^2 = |a|^2 + |b|^2 + 2 * a · b
a · b = |a| * |b| * cosΘ


따라서 직각삼각형일 땐 피타고라스 가능
(a + b)^2 = |a|^2 + |b|^2

ex)
|dir|^2 = |dirX|^2 + |dirY|^2

 

1

 

double dirX = -1, dirY = 0;
double planeX = 0, planeY = 0.66;
방향벡터와 수직이기만 하면 변수선언 시 카메라평면의 길이는 다양할 수 있습니다.

벡터와 수직이다 :  서로 다른 쪽(x or y)이 0이면 두 벡터는 수직이다. (백터의 내적)

따라서 "dirY = 0이기 때문에 planeX = 0이기만 하면 planeY는 다양할 수 있다." 로 변환 가능

 

 

초기선언 이후에는, 입력키로 회전해서 방향벡터 dir 과 카메라평면 plane 의 값이 변경되더라도 
항상 서로 수직이어야하고 동일한 길이가 유지되어야 합니다.

방향벡터와 카메라평면은 회전행렬을 이용해서 동시에 방향을 바꾸는 것만 가능.

임의로 dir와 plane의 절댓값을 바꾸면 안된다. (처음 선언대로 dir의 절댓값은 1, plane의 절댓값은 0.66 유지)

 


 

2

deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))

But this can be simplified to:

deltaDistX = abs(|v| / rayDirX)
deltaDistY = abs(|v| / rayDirY)
However, we can use 1 instead of |v|, 
because only the *ratio* between deltaDistX and deltaDistY matters for the DDA code that follows later below, 

 

|v| 대신 1을 쓸 수 있는 이유:

|v| = sqrt(rayDirX * raDirX + rayDirY * rayDirY) 로 정확한 값을 갖고 있지만

deltaDistX와 deltaDistY를 이용해서 sideDistX와 sideDistY를 비교하는 과정에서는 정확한 값이 필요하지 않기 때문에 (deltaDistX와 deltaDistY의 *비율*만 중요하기 때문에)

편하게 1로 대체. 1이 아니라 정확한 값을 그대로 넣어도 되고, 다른 어떤 상수를 넣어도 상관 없음.

 


 

3

 Note that its length |rayDir| is not 1 but slightly higher, 
 due to how we added a value to (dirX,dirY) (the dir vector, which is normalized to 1) in the code.

2번 보충. |v| (|rayDir|)가 실제로 1이 아니라 조금 큰 값이라고 설명되어있음.

 

 

유도 과정

rayDirX = dirX + planeX * cameraX;
rayDirY = dirY + planeY * cameraX;

양변 제곱 후 더하기
(rayDirX)^2 + (rayDirY)^2 = (dirX + planeX * camX)^2 + (dirY + palneY * camX)^2
(rayDir)^2 = (dirX)^2 + (2 * dirX * planeX * camX) + (planeX * camX)^2
           + (dirY)^2 + (2 * dirY * planeY * camX) + (planeY * camX)^2
(rayDir)^2 = (dirX)^2 + (dirY)^2 + 0 + ((planeX)^2 + (planeY)^2) * (camX)*2
           = (dir)^2 + 0 + (plane)^2 * (camX)^2
            
(camX 범위 : -1 ~ 1)
(rayDir)^2 = (1)^2 + (0.66)^2 * (0 ~ 1)
(rayDir)^2 = 1 + (0.4356) * (0 ~ 1) 
           = (1 ~ 1.4356)
|rayDir| = (1 ~ 1.1981)

 


 

4

yDist : E = |rayY| : |ray|
perpWallDist : |dir| = E : |ray|

~비례식 정리~

perpWallDist = yDist / |rayY|
길이 = 길이 / 길이 → OK

↓
perpWallDist = yDist / rayY
길이 = 길이 / 벡터 → why?

|rayY|의 절댓값을 뗄 수 있는 이유

 

애초에 yDist도 항상 양수(길이)인 것이 아니기 때문

yDist = (mapY - posY + (1 - stepY) / 2)
mapY는 이전 단계에서 stepY를 여러번 더한 값으로, stepY는 +1 또는 -1

stepY = 1 (raydirY > 0)이면 yDist는 양수
stepY = 0 (rayDirY < 0)이면 yDist는 음수

따라서 |rayDirY|가 아니라 rayDirY로 나눠야 perpWallDist가 항상 양수(길이)가 나온다
perpWallDist = yDist / rayDirY

 


5

draw_start = MAX(h / 2 - lineHeight / 2, 0);
draw_end = MIN(h / 2 + lineHeight / 2, h);

lineHeight를 구해서 화면의 절반(h/2)을 중심으로 위아래 대칭으로 벽을 그린다.

MAX와 MIN은 start와 end가 화면 밖으로 넘어갈 때 넘어가지 않도록 보정해주는 과정.

 

로데브에서는 MIN에 h 대신 h - 1 가 들어가있다. 하지만 h - 1 을 넣으면 1픽셀만큼 벽이 덜 그려지는데 왜 - 1 해준지 모르겠음.

나는 h로 

 


6

double wallX;

if (side == 0) 
    wallX = posY + perpWallDist * rayDirY;
else
    wallX = posX + perpWallDist * rayDirX;

 

는 아래와 동일한 식

if (side == 0) 
    wallX = posY + yDist;
else
    wallX = posX + xDist;

 

 


카메라행렬(sprite)

 

1

즉, 스프라이트의 위치에서 플레이어의 위치를 뺀 다음에, 그것을 카메라의 (2 x 2)크기 역행렬과 곱합니다.

1. 위치끼리 빼는 이유: 플레이어에 대한 스프라이트의 상대적 위치를 구한다

2. 카메라 역행렬을 곱하는 이유: 플레이어의 회전에 따라 스프라이트도 회전해야 하기 때문 (항상 같은 면을 보여준다)

 

 

x : 스프라이트 2d 위치

P : 카메라 행렬

X : 회전을 적용한(?) 스프라이트 3d 위치

 

double transformX = invDet * (dirY * spriteX - dirX * spriteY);
double transformY = invDet * (-planeY * spriteX + planeX * spriteY);

 


2

 

스프라이트 그리는 방식:

스프라이트 가로길이의 중간을 기준으로 해서 - width / 2  ~ + width / 2 범위만큼 그린다

스프라이트 세로길이 중간을 기준으로 해서 - height / 2 ~ + height / 2 범위만큼 그린다.

 

 

가로)

int spriteScreenX = int((w / 2) * (1 + transformX / transformY));

spriteScreenX : 스프라이트 가로길이 중간 좌표

transformX : 화면 안쪽에 들어가 있는 스프라이트 위치

transformY : 스프라이트가 화면 안쪽으로 들어간 깊이

 

www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/mathematics-computing-2d-coordinates-of-3d-points

 

transformX / transformY : 화면 안쪽에 있는 스프라이트를 화면 위로 가져옴. 그림에서 C -> C'

 

(w / 2) * (1 + transformX / transformY) : 실제 출력 좌표 계산 (뷰포트 변환: celdee.tistory.com/531)

 

 

 

세로)

 

스프라이트 세로길이 중간 좌표는 기본적으로 window_height / 2

여기에 vMoveScreen을 더해서 기준점을 상하로 움직일 수 있다.


3

 

uDiv : 스프라이트 폭 조절

스프라이트_width를 uDiv로 나눠주면 홀쭉해지거나 뚱뚱해진다.

 

vDiv : 스프라이트 높이 조절

스프라이트_height를 vDiv로 나눠주면 길어지거나 납작해진다.

 

 

'42 > cub3d' 카테고리의 다른 글

cub3d - 완성본  (0) 2020.10.19
cub3d - transparency  (0) 2020.10.17
cub3d - wall  (0) 2020.10.15
cub3d - mlx_get_data_addr  (0) 2020.09.23
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함