如何解决Raycaster中近距离斜向墙面的弯曲畸变问题
Raycaster墙体渲染畸变问题修复方案
问题现象
斜向站立在长墙附近时,近距离墙面向内弯曲,远处墙面略微向外弯曲;正视墙面时无此问题。已尝试distance = distance * cos(ca)距离修正、配置camera plane,降低FOV可缓解但需保留FOV滑块功能。
问题根源
- 当前采用角度线性步进生成射线,导致边缘射线在相机平面上的间距不均匀,引发透视畸变(真实透视需基于相机平面的均匀采样)
- 墙高计算的
SCALE_FACTOR为固定值,未与FOV关联,不同FOV下会出现比例失调 - 角度差
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
相关产品推荐
相关产品推荐

