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

为何玩家靠近墙壁时光线投射器会出现性能下降?

问题描述

我正在开发一款2.5D游戏,发现当玩家靠近并面朝墙壁时,游戏会出现严重卡顿,添加纹理后该问题进一步加剧。我清楚当前使用的渲染方法效率较低,且已研究过通用优化方案,因此本次并非询问整体优化方法。

我原本认为卡顿只是因为屏幕显示内容过多,这一想法部分正确,但我已确保渲染器会裁剪屏幕外的内容,还统计了每帧绘制的像素(矩形)数量。统计后发现,即便绘制的矩形数量完全相同,玩家越靠近墙壁,游戏运行越慢。请问为何这段代码在绘制相同数量矩形时仍会变慢?

相关代码

int drawStart = y_offset > 0 ? y_offset : 0;
int drawEnd = (y_offset + bar_height) < 480 ? y_offset + bar_height : 480;
int texNum = map[cell_y][cell_x] - 49;

//calculate value of wallX
double wallX; //where exactly the wall was hit
if (side == 0) wallX = player->y + distance * ray_angle_y;
else           wallX = player->x + distance * ray_angle_x;
wallX -= floor(wallX);

//x coordinate on the texture
int texX = wallX * TEXTURE_WIDTH;
if (side == 0 && ray_angle_x > 0) texX = TEXTURE_WIDTH - texX - 1;
if (side == 1 && ray_angle_y < 0) texX = TEXTURE_WIDTH - texX - 1;

// How much to increase the texture coordinate per screen pixel
double step = 1.0 * TEXTURE_HEIGHT / bar_height;
double pixel_size = bar_height / TEXTURE_HEIGHT;
// Starting texture coordinate
double texPos = bar_height > 480 ? (-y_offset * step) : 0;
for(int y = drawStart; y < drawEnd; y++) {
    // Cast the texture coordinate to integer, and mask with (TEXTURE_HEIGHT - 1) in case of overflow
    int texY = (int)texPos & (TEXTURE_HEIGHT - 1);
    texPos += step;
    Uint32 color = textures[texNum].array[texY][texX];
    if(side == 1) color = (color >> 1) & 8355711;
    
    
    SDL_Rect rect = {i * rect_width, y, rect_width, pixel_size};
 /* even when this is called the same amount of times, it will be slower depending on how close to the wall the player is*/
    SDL_FillRect( screenSurface, &rect, color );
}

问题根源与分析

核心原因是每次SDL_FillRect调用实际填充的像素总量差异巨大,你统计的只是矩形数量,忽略了单个矩形的面积变化:

  1. 单个矩形的像素填充量随距离变化
    当玩家靠近墙壁时,墙壁在屏幕上的高度bar_height会显著增大(射线投射的特性:距离越近,物体的屏幕高度越高)。代码中pixel_size = bar_height / TEXTURE_HEIGHT,意味着单个矩形的高度会随bar_height增大而变大:

    • 玩家离墙远时,bar_height小,pixel_size可能为1或更小,每个SDL_FillRect仅填充几行像素;
    • 玩家靠近墙时,bar_height可能达到屏幕高度(480),pixel_size会变成480 / TEXTURE_HEIGHT(假设纹理高度256,pixel_size≈1.875),每个矩形要填充近2行像素。
      虽然循环次数(矩形数量)相同,但总像素填充量会随玩家靠近成倍数增长,直接导致CPU开销剧增。
  2. SDL_FillRect的额外开销
    每次调用SDL_FillRect都有参数校验、内存地址计算等固定开销。当单个矩形的填充量变大时,内部的像素填充逻辑(内存块拷贝、颜色值写入)耗时会线性增长。另外,将double类型的pixel_size赋值给SDL_Rect.h(整数类型)会触发隐式类型转换,若pixel_size非整数,SDL可能需要处理像素边界对齐,进一步增加耗时。

  3. 纹理采样的间接影响
    添加纹理后卡顿加剧,核心还是填充像素总量的问题。纹理采样本身的开销(texY计算、颜色值读取)在靠近墙壁时并没有显著变化,真正的瓶颈还是SDL_FillRect的像素填充操作。

针对性改进思路(不涉及整体渲染架构改动)

针对这段代码可以做以下调整,消除矩形高度变化带来的开销差异:

  • 直接操作屏幕缓冲区:使用SDL_LockSurface锁定screenSurface,直接计算并写入整列像素数据,避免多次调用SDL_FillRect,消除函数调用开销,同时内存访问更连续,缓存效率更高。
  • 预计算整列颜色数据:先循环计算当前竖条所有y坐标对应的颜色值,再一次性写入屏幕缓冲区。

示例代码片段(简化版):

// 锁定屏幕缓冲区
if (SDL_LockSurface(screenSurface) != 0) {
    // 处理错误
    return;
}

Uint32* pixels = (Uint32*)screenSurface->pixels;
int pitch = screenSurface->pitch / sizeof(Uint32);
int startX = i * rect_width;

for(int y = drawStart; y < drawEnd; y++) {
    int texY = (int)texPos & (TEXTURE_HEIGHT - 1);
    texPos += step;
    Uint32 color = textures[texNum].array[texY][texX];
    if(side == 1) color = (color >> 1) & 8355711;
    
    // 直接写入整列的rect_width个像素
    for (int x = 0; x < rect_width; x++) {
        pixels[y * pitch + startX + x] = color;
    }
}

SDL_UnlockSurface(screenSurface);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 19:31:05