Fisheye Raycasting余弦校正不适用时的鱼眼畸变修复问题
Raycasting游戏鱼眼畸变修复方案
问题现状
开发简易光线投射游戏时,基于光线长度、对数公式计算墙体尺寸做渲染,出现鱼眼畸变问题,常规余弦校正无法适配现有项目架构,核心实现代码如下:
void draw_3d(t_cub3d *this, double len, int num_of_ray, double start) { double height; double width; double x; double y; height = 720 / log10(len + 1); if (height > 720) height = 720; width = 1280.0 / 640; y = (720 - height) / 2; while (y < height) { x = 2 * num_of_ray; while(x < 2 + width * num_of_ray) { my_mlx_pixel_put(this, (int)x, (int)y, 0xfffff); x++; } y++; } } int raycast(t_cub3d *this) { double start = this->raycast->pa - this->raycast->fov; double end = this->raycast->pa + this->raycast->fov; double ray_p_x; double ray_p_y; double len; int num_of_ray; num_of_ray = 0; while (start <= end) { ray_p_x = this->raycast->px; ray_p_y = this->raycast->py; while (this->gameinfo->map[(int) ray_p_y / 30][(int) ray_p_x / 30] != '1' && this->gameinfo->map[(int) ray_p_y / 30][(int) ray_p_x / 30] != ' ') { ray_p_x += cos(start); ray_p_y += sin(start); } len = fabs(this->raycast->px - ray_p_x) / fabs(cos(start)); len *= cos(this->raycast->pa - start); start += this->raycast->fov / 320; draw_3d(this, len, num_of_ray, start); num_of_ray++; } mlx_put_image_to_window(this->mlx_info->mlx, this->mlx_info->mlx_win, this->img->img, 0, 0); return (0); }
程序运行时直墙会在视野边缘向外弧形凸起,呈现典型鱼眼畸变效果。
畸变根因
余弦校正失效不是方案本身问题,是现有代码存在3个逻辑错误:
- 光线长度计算错误:用x轴坐标差反推斜距的方式精度差,当光线接近水平/垂直时容易出现除零、数值溢出问题,算出来的原始长度本身不准
- 角度差未做归一化:光线角度和玩家朝向角度的差值可能超出余弦函数的有效计算范围,导致校正系数错误
- 墙体绘制循环边界错误:y轴绘制的终止值设置错误,墙体下半部分无法正常绘制,进一步放大了畸变的视觉表现
适配现有架构的修复步骤
不需要重构现有渲染逻辑,仅修改局部代码即可:
- 替换光线长度计算逻辑,直接计算玩家到碰撞点的欧氏距离作为原始斜距,替换原有len计算的第一行代码:
// 替换 len = fabs(this->raycast->px - ray_p_x) / fabs(cos(start)); len = sqrt(pow(ray_p_x - this->raycast->px, 2) + pow(ray_p_y - this->raycast->py, 2));
- 增加角度差归一化逻辑,修正余弦校正系数的计算准确性,替换原有len乘校正系数的代码:
// 替换 len *= cos(this->raycast->pa - start); double angle_diff = this->raycast->pa - start; // 归一化角度差到[-π, π]区间,避免cos计算值错误 while (angle_diff > M_PI) angle_diff -= 2 * M_PI; while (angle_diff < -M_PI) angle_diff += 2 * M_PI; len *= cos(angle_diff);
- 修正draw_3d函数中y轴循环的终止条件,原有条件会导致墙体只绘制上半段:
// 把循环条件从 while (y < height) 改为 while (y < (720 + height) / 2)
- 原有对数计算墙体高度的逻辑完全不需要修改,校正后的len值可以直接传入计算,适配现有架构。
修复后效果
视野内的直墙边缘会保持竖直,不会再出现两侧向外凸起的弧形畸变,和标准光线投射渲染效果一致。
内容的提问来源于stack exchange,提问作者deliriumgrim
相关产品推荐
相关产品推荐

