You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Raycaster中近距离斜向墙面的弯曲畸变问题

Raycaster墙体渲染畸变问题修复方案

问题现象

斜向站立在长墙附近时,近距离墙面向内弯曲,远处墙面略微向外弯曲;正视墙面时无此问题。已尝试distance = distance * cos(ca)距离修正、配置camera plane,降低FOV可缓解但需保留FOV滑块功能。

问题根源

  1. 当前采用角度线性步进生成射线,导致边缘射线在相机平面上的间距不均匀,引发透视畸变(真实透视需基于相机平面的均匀采样)
  2. 墙高计算的SCALE_FACTOR为固定值,未与FOV关联,不同FOV下会出现比例失调
  3. 角度差ca的范围未修正到[-π, π],可能导致cos(ca)计算错误

修复步骤

1. 替换角度线性步进为相机平面均匀采样

修改walls_3d函数,直接按屏幕宽度遍历,基于屏幕x坐标计算对应射线角度,确保相机平面采样均匀:

void    walls_3d(t_minimap *map, t_player_data *player, t_mlx *mlx)
{
    double  angle;
    int     i;
    double  camera_x; // 相机平面x坐标,范围[-1, 1]
    t_ray   ray_result;

    i = 0;
    while (i < SCREEN_WIDTH)
    {
        // 计算当前屏幕列对应的相机平面位置
        camera_x = 2 * i / (double)SCREEN_WIDTH - 1;
        // 基于相机平面偏移计算射线角度(1.0为相机到平面的焦距)
        angle = player->playerangle + atan2(camera_x, 1.0);
        
        // 修正角度到[0, 2π]范围
        if (angle < 0)
            angle += 2 * M_PI;
        if (angle > 2 * M_PI)
            angle -= 2 * M_PI;

        draw_rays(player, map, angle, &ray_result);
        draw_wall_segment_to_image(i, ray_result.distance, mlx, ray_result.color);
        i++;
    }
}

2. 关联FOV与墙高计算的焦距

修改draw_wall_segment_to_image,用FOV计算焦距替代固定SCALE_FACTOR,确保不同FOV下墙高比例正确:

void    draw_wall_segment_to_image(int x, float distance, t_mlx *mlx, int color)
{
    int wall_height;
    int y_start;
    int y_end;
    t_player_data *player = mlx->map->player;
    double fov_rad = player->fov * ONE_DEGREE;
    // 计算焦距:基于FOV和屏幕高度的透视参数
    double focal_length = SCREEN_HEIGHT / (2 * tan(fov_rad / 2));
    
    wall_height = (int)(focal_length / distance);
    y_start = (SCREEN_HEIGHT - wall_height) / 2;
    y_end = y_start + wall_height;

    // 修正超出屏幕范围的墙高
    if (y_start < 0) y_start = 0;
    if (y_end >= SCREEN_HEIGHT) y_end = SCREEN_HEIGHT - 1;

    mlx->wall_color = color;
    // 每列宽度为1,直接绘制单列
    draw_ceiling(x, 1, y_start, mlx);
    draw_walls(x, y_start, y_end, mlx);
    draw_floor(x, 1, y_end, mlx);
}

3. 修正角度差ca的范围计算

确保draw_rays中ca的范围在[-π, π],避免cos(ca)计算错误:

void    draw_rays(t_player_data *player, t_minimap *map, double angle, t_ray *rs)
{
    t_ray_data      ray_data;
    t_ray_result    result_h;
    t_ray_result    result_v;
    double          ca;

    ray_data.ra = angle;
    ca = player->playerangle - ray_data.ra;
    // 修正角度差到[-π, π]范围
    ca = fmod(ca, 2 * M_PI);
    if (ca > M_PI) ca -= 2 * M_PI;
    if (ca < -M_PI) ca += 2 * M_PI;

    // 原有水平/垂直射线计算逻辑保留...
    
    rs->distance = mes_dist(player->posx, player->posy, ray_data.rx, ray_data.ry);
    rs->distance = rs->distance * cos(ca);
}

4. 调整绘制函数的宽度参数

修改draw_walls、draw_ceiling、draw_floor,直接使用传入的segment_width参数,不再重新计算:

void    draw_walls(int x, int y_start, int y_end, t_mlx *mlx, int segment_width)
{
    int y;
    int x_current;

    x_current = x;
    while (x_current < x + segment_width)
    {
        y = y_start;
        while (y < y_end)
        {
            my_mlx_pixel_put(mlx, x_current, y, mlx->wall_color);
            y++;
        }
        x_current++;
    }
}

(draw_ceiling和draw_floor做同样修改,函数参数增加segment_width,并在调用时传入)

原理说明

  • 相机平面均匀采样:真实透视中,屏幕上的每一列对应相机平面上的均匀分布点,而非均匀角度的射线,这是消除边缘畸变的核心
  • 焦距与FOV关联:焦距由FOV和屏幕高度计算得出,确保不同FOV下墙高的透视比例一致
  • 角度差范围修正:保证cos(ca)始终计算的是射线与玩家视角的最小夹角,距离修正值准确

内容的提问来源于stack exchange,提问作者Quinten Raymaekers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 14:05:16