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

基于MinilibX的Raycasting纹理垂直拉伸及距离异常问题排查

MinilibX Raycasting游戏纹理垂直拉伸问题修复方案

问题背景

在使用MinilibX开发基于Raycasting算法的小游戏时,墙体渲染位置、尺寸正确,但64x64规格的纹理出现垂直方向拉伸扭曲(spaghettified)现象。将line_height计算从float line_height = (S_H / wall_dist * 64);改为float line_height = (S_H / wall_dist);后,拉伸问题解决,但墙体显示距离玩家过远,需要调整。

原渲染函数代码

void draw_line(t_player *player, t_game *game, float start_x, int i)
{
    float ray_dir_x = cos(start_x);
    float ray_dir_y = sin(start_x);
    float map_x = player->p_x;
    float map_y = player->p_y;
    double delta_dist_x = fabs(1 / ray_dir_x);
    double delta_dist_y = fabs(1 / ray_dir_y);
    double side_dist_x;
    double side_dist_y;

    int step_x;
    int step_y;
    if (ray_dir_x < 0) {
        step_x = -1;
        side_dist_x = (player->p_x - map_x) * delta_dist_x;
    } else {
        step_x = 1;
        side_dist_x = (map_x + 1.0 - player->p_x) * delta_dist_x;
    }
    if (ray_dir_y < 0) {
        step_y = -1;
        side_dist_y = (player->p_y - map_y) * delta_dist_y;
    } else {
        step_y = 1;
        side_dist_y = (map_y + 1.0 - player->p_y) * delta_dist_y;
    }

    int side;
    while (!touch(map_x, map_y, game)) {
        if (side_dist_x < side_dist_y) {
            side_dist_x += delta_dist_x;
            map_x += step_x;
            side = 0;
        } else {
            side_dist_y += delta_dist_y;
            map_y += step_y;
            side = 1;
        }
    }

    double wall_dist;
    double wall_x;
    if (side == 0)
        wall_dist = (map_x - player->p_x + (1 - step_x) / 2) / ray_dir_x;
    else
        wall_dist = (map_y - player->p_y + (1 - step_y) / 2) / ray_dir_y;

  //clear
   float line_height = (S_H / wall_dist * 64);  // Correct line height calculation

    // Adjust line height based on distance and projection plane
    //float proj_plane_dist = (float)S_W / (2 * tan(FOV / 2)); // Projection plane distance
    //float line_height = (proj_plane_dist / wall_dist); // Use consistent scaling
    int draw_start = -(int)line_height / 2 + S_H / 2;
    if (draw_start < 0)
        draw_start = 0;
    int draw_end = line_height / 2 + S_H / 2;
    if (draw_end >= S_H)
        draw_end = S_H - 1;

    if (side == 0)
        wall_x = (player->p_y) + wall_dist * ray_dir_y;
    else
        wall_x = (player->p_x) + wall_dist * ray_dir_x;
  //  wall_x -= floor(wall_x);  // Ensure it's a fractional part between 0 and 1

    //printf("wall_x: %f\n", wall_x);
    // Select the correct texture based on ray direction
    int *texture;
    if (side == 0) {
        if (ray_dir_x > 0)
            texture = game->texture_e.text_value;  // East
        else
            texture = game->texture_w.text_value;  // West
    } else {
        if (ray_dir_y > 0)
            texture = game->texture_s.text_value;  // South
        else
            texture = game->texture_n.text_value;  // North
    }

    printf("p_x: %f, p_y: %f, wall_x: %f\n", player->p_x,player->p_y, wall_x);


    int tex_x = (int)(wall_x * 64);  // Adjust texture X
    tex_x = tex_x % 64;  // Ensure tex_x is within bounds (0-63)
    // Draw the texture column
    for (int y = draw_start; y < draw_end; y++) {
        int tex_y = (int)((y - draw_start) * 64.0 / line_height) % 64;  // Adjust texture Y

        // Debug output
       // printf("tex_x: %d, tex_y: %d, wall_x: %f\n", tex_x, tex_y, wall_x);

        int color = texture[tex_x + tex_y * 64];  // Access the texture pixel
        put_pixel(i, y, color, game);  // Draw the pixel on the screen
    }
    //i++;
}

调试输出

p_x: 760.554504, p_y: 268.357147, wall_x: 280.284412
p_x: 760.554504, p_y: 268.357147, wall_x: 280.252147
p_x: 760.554504, p_y: 268.357147, wall_x: 280.219890
p_x: 760.554504, p_y: 268.357147, wall_x: 280.187640
p_x: 760.554504, p_y: 268.357147, wall_x: 280.155396
Window Closed

核心问题分析

  1. 鱼眼效应未修正:当前wall_dist是射线实际长度,而非垂直于玩家投影平面的距离,导致边缘墙体距离被高估,修改line_height后显示过远。
  2. line_height计算错误:原代码多乘64导致纹理Y采样间隔异常,造成拉伸;去掉64后又缺少地图格子尺寸的缩放,导致墙体比例失调。
  3. wall_x未取小数部分:注释掉的wall_x -= floor(wall_x);导致纹理X坐标逻辑错误,虽暂时用%64凑出正确值,但不符合Raycasting采样逻辑。

修复方案

1. 修正wall_dist的鱼眼效应

在计算完wall_dist后添加角度修正:

// 修正鱼眼效应,start_x为当前射线角度,player->angle为玩家朝向角度
double angle_diff = start_x - player->angle;
if (angle_diff > M_PI) angle_diff -= 2*M_PI;
if (angle_diff < -M_PI) angle_diff += 2*M_PI;
wall_dist *= cos(angle_diff);

2. 正确计算line_height

结合地图格子尺寸(64)和修正后的距离:

// 替换原line_height计算
float line_height = (64.0 * S_H) / wall_dist;

3. 恢复wall_x小数部分提取

取消注释该行代码:

wall_x -= floor(wall_x); // 提取0~1的小数部分,用于纹理X坐标采样

4. 修正tex_x计算

基于小数部分计算纹理X坐标:

int tex_x = (int)(wall_x * 64.0);
tex_x = tex_x % 64; // 可选,防止浮点精度溢出

修复后关键代码片段

// ... 射线检测代码不变 ...

double wall_dist;
double wall_x;
if (side == 0)
    wall_dist = (map_x - player->p_x + (1 - step_x) / 2) / ray_dir_x;
else
    wall_dist = (map_y - player->p_y + (1 - step_y) / 2) / ray_dir_y;

// 新增:修正鱼眼效应
double angle_diff = start_x - player->angle;
if (angle_diff > M_PI) angle_diff -= 2*M_PI;
if (angle_diff < -M_PI) angle_diff += 2*M_PI;
wall_dist *= cos(angle_diff);

// 正确计算line_height
float line_height = (64.0 * S_H) / wall_dist;

int draw_start = -(int)line_height / 2 + S_H / 2;
if (draw_start < 0)
    draw_start = 0;
int draw_end = line_height / 2 + S_H / 2;
if (draw_end >= S_H)
    draw_end = S_H - 1;

if (side == 0)
    wall_x = (player->p_y) + wall_dist * ray_dir_y;
else
    wall_x = (player->p_x) + wall_dist * ray_dir_x;

// 恢复提取小数部分
wall_x -= floor(wall_x);

// ... 纹理选择代码不变 ...

// 修正tex_x计算
int tex_x = (int)(wall_x * 64.0);
tex_x = tex_x % 64;

// 绘制纹理列
for (int y = draw_start; y < draw_end; y++) {
    int tex_y = (int)((y - draw_start) * 64.0 / line_height) % 64;
    int color = texture[tex_x + tex_y * 64];
    put_pixel(i, y, color, game);
}

修复逻辑说明

  • 鱼眼修正:确保所有墙体使用垂直于投影平面的距离计算高度,避免边缘墙体比例失真。
  • line_height缩放:用地图格子尺寸(64)适配屏幕高度,保证不同距离的墙体显示比例一致。
  • 纹理采样逻辑:提取wall_x小数部分,确保从墙体表面的相对位置采样纹理,符合Raycasting映射规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:34:55