Hi yoahn 개발블로그

[42Seoul/cub3D] #3 벽과 바닥 raycasting 본문

42 SEOUL/배운 것들 정리

[42Seoul/cub3D] #3 벽과 바닥 raycasting

hi._.0seon 2021. 3. 31. 16:04
반응형

텍스처 그리기와 동일한 코드에서 바닥과 천장 캐스팅하는 부분을 추가한다.

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

 

l-yohai/cub3d

Porting by Lode's Computer Graphics Tutorial - Raycasting to C and Minilibx for 42 Subject Cub3D - l-yohai/cub3d

github.com

void    calc(t_game *game)
{
    // floor casting
    for (int y = 0;y < screenHeight;y++)
    {
        float rayDirX0 = game->dirX - game->planeX;
        float rayDirY0 = game->dirY - game->planeY;
        float rayDirX1 = game->dirX + game->planeX;
        float rayDirY1 = game->dirY + game->planeY;

        // 현재 y 위치와 스크린 높이 중앙 위치를 비교
        int p = y - screenHeight / 2;

        // camera 수직 위치
        float posZ = 0.5 * screenHeight;
        float rowDistance = posZ / p;

        float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / screenWidth;
        float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / screenWidth;

        float floorX = game->posX + rowDistance * rayDirX0;
        float floorY = game->posY + rowDistance * rayDirY0;

        for (int x = 0;x < screenWidth;x++)
        {
            int cellX = (int)floorX;
            int cellY = (int)floorY;

            int tx = (int)(texWidth * (floorX - cellX)) & (texWidth - 1);
            int ty = (int)(texHeight * (floorY - cellY)) & (texHeight - 1);
            
            floorX += floorStepX;
            floorY += floorStepY;

            int floorTexture = 3;
            int ceilingTexture = 6;

            int color;

            color = game->texture[floorTexture][texWidth * ty + tx];
            color = (color >> 1) & 8355711; // 어둡게 표현

            game->buf[y][x] = color;

            color = game->texture[ceilingTexture][texWidth * ty + tx];
            color = (color >> 1) & 8355711;

            game->buf[screenHeight - y - 1][x] = color;
        }
    }

posZ : 광선 시작점

y = (0 ~ screenHeight/2)

p : y와 스크린 중앙의 차이값

 

화면을 가로로 반 나눠서 위에는 천장 텍스처를 그리고, 아래에는 바닥 텍스처를 그린다.

y 가 height / 2 여도 같은 결과가 나온다.

 

이 루프의 결과는 이동과 상관없이 화면을 절반씩 나눠 천장과 바닥을 그리는 것이므로 그려진 화면에서 이동하게 되면 바닥과 천장은 움직이지 않아 플레이어가 움직이는 것이 아니라 벽이 이동하는 것처럼 보이게 된다.

 

github.com/p-eye/cub3d_texturing/blob/master/3.ceil.md

 

p-eye/cub3d_texturing

Description of cub3D texturing. Contribute to p-eye/cub3d_texturing development by creating an account on GitHub.

github.com

벽 raycasting 이전에 했던 것과 같다.

    for (int x = 0;x < screenWidth;x++)
    {
        double cameraX = 2 * x / (double)screenWidth - 1;
        double rayDirX = game->dirX + game->planeX * cameraX;
        double rayDirY = game->dirY + game->planeY * cameraX;

        int mapX = (int)game->posX;
        int mapY = (int)game->posY;

        double sideDistX, sideDistY;

        double deltaDistX = fabs(1 / rayDirX);
        double deltaDistY = fabs(1 / rayDirY);
        double perpWallDist;

        int stepX, stepY;

        int hit = 0;
        int side;

        if (rayDirX < 0)
        {
            stepX = -1;
            sideDistX = (game->posX - mapX) * deltaDistX;
        }
        else
        {
            stepX = 1;
            sideDistX = (mapX + 1.0 - game->posX) * deltaDistX;
        }
        if (rayDirY < 0)
        {
            stepY = -1;
            sideDistY = (game->posY - mapY) * deltaDistY;
        }
        else
        {
            stepY = 1;
            sideDistY = (mapY + 1.0 - game->posY) * deltaDistY;
        }

        while (hit == 0)
        {
            if (sideDistX < sideDistY)
            {
                sideDistX += deltaDistX;
                mapX += stepX;
                side = 0;
            }
            else
            {
                sideDistY += deltaDistY;
                mapY += stepY;
                side = 1;
            }
            if (worldMap[mapX][mapY] > 0) hit = 1;
        }
        if (side == 0)
            perpWallDist = (mapX - game->posX + (1 - stepX) / 2) / rayDirX;
        else
            perpWallDist = (mapY - game->posY + (1 - stepY) / 2) / rayDirY;

        int lineHeight = (int)(screenHeight / perpWallDist);

        int drawStart = -lineHeight / 2 + screenHeight / 2;
        if (drawStart < 0)
            drawStart = 0;
        int drawEnd = lineHeight / 2 + screenHeight / 2;
        if (drawEnd >= screenHeight)
            drawEnd = screenHeight - 1;

        int texNum = worldMap[mapX][mapY] - 1;

        double wallX;
        if (side == 0)
            wallX = game->posY + perpWallDist * rayDirY;
        else
            wallX = game->posX + perpWallDist * rayDirX;
        wallX -= floor(wallX);

        int texX = (int)(wallX * (double)texWidth);
        if (side == 0 && rayDirX > 0)
            texX = texWidth - texX - 1;
        if (side == 1 && rayDirY < 0)
            texX = texWidth - texX - 1;

        // How much to increase the texture coordinate per screen pixel
        double step = 1.0 * texHeight / lineHeight;

        // starting texture coordinate
        double texPos = (drawStart - screenHeight / 2 + lineHeight / 2) * step;

        for (int y = drawStart; y < drawEnd;y++)
        {
            int texY = (int)texPos & (texHeight - 1);
            texPos += step;

            int color = game->texture[texNum][texWidth * texY + texX];

            if (side == 1)
                color = (color >> 1) & 8355711;

            game->buf[y][x] = color;
        }

벽이 그려지는 범위인 drawStart - drawEnd 이외의 범위에 천장과 바닥을 그린다.

        // floor casting
        double floorXWall, floorYWall;

        if (side == 0 && rayDirX > 0)
        {
            floorXWall = mapX;
            floorYWall = mapY + wallX;
        }
        else if (side == 0 && rayDirX < 0)
        {
            floorXWall = mapX + 1.0;
            floorYWall = mapY + wallX;
        }
        else if (side == 1 && rayDirY > 0)
        {
            floorXWall = mapX + wallX;
            floorYWall = mapY;
        }
        else
        {
            floorXWall = mapX + wallX;
            floorYWall = mapY + 1.0;
        }

        double distWall, distPlayer, currentDist;

        distWall = perpWallDist;
        distPlayer = 0.0;
        
        if (drawEnd < 0) drawEnd = screenHeight;

        for (int y = drawEnd + 1; y < screenHeight; y++)
        {
            currentDist = screenHeight / (2.0 * y - screenHeight);

            double weight = (currentDist - distPlayer) / (distWall - distPlayer);
            double currentFloorX = weight * floorXWall + (1.0 - weight) * game->posX;
            double currentFloorY = weight * floorYWall + (1.0 - weight) * game->posY;

            int floorTexX, floorTexY;
            floorTexX = (int)(currentFloorX * texWidth) % texWidth;
            floorTexY = (int)(currentFloorY * texHeight) % texHeight;

            int checkerBoardPattern = ((int)(currentFloorX) + (int)(currentFloorY)) % 2;
            int floorTexture;
            if (checkerBoardPattern == 0) floorTexture = 3;
            else floorTexture = 4;

            game->buf[y][x] = (game->texture[floorTexture][texWidth * floorTexY + floorTexX] >> 1) & 8355711;
            game->buf[screenHeight - y][x] = game->texture[6][texWidth * floorTexY + floorTexX];
        }
    }
}

if side == 0 && rayDirX > 0

벽에  부딪힌 면이 Y축 면이고 광선 방향이 양수이면

바닥의 x 축 값은 현재 mapX 값이고

바닥의 y 축 값은 현재 mapY + wallX

(wallX가 부딪힌 벽에서 정확한 위치값을 나타냄)

 

if side == 0 && rayDirX < 0

벽에 부딪힌 면이 y축이고 광선의 방향이 음수이면 

바닥의 x축 값은 mapX + 1.0

바닥의 y축 값은 mapY + wallX

 

반응형
Comments