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

基于GLFW的C语言Raycaster开发求助:射线距离计算与画线问题

问题描述

我正在用C语言结合GLFW库开发简易Raycaster,卡在了update函数末尾的画线部分。我认为错误源于不合理的距离计算算法,请求帮忙排查该算法问题,同时指出代码中的其他错误。附上的代码包含地图、窗口创建、相机、墙体探测射线、距离计算算法等模块,除距离计算外其余模块看似正常运行。

代码

#include <stdio.h>
#include <GLFW/glfw3.h>
#include <math.h>

#define SCREENWIDTH 1920
#define SCREENHEIGHT 1080
#define MAPWIDTH 24
#define MAPHEIGHT 24

float boxPositionX = MAPWIDTH;
float boxPositionY = MAPHEIGHT;
float boxSize = 5.0f;

double posX = 22, posY = 12;  //x and y start position
double dirX = -1, dirY = 0; //initial direction vector
double planeX = 0, planeY = 0.66; //the 2d raycaster version of camera plane

double time = 0; //time of current frame
double oldTime = 0; //time of previous frame

int worldMap[MAPWIDTH][MAPHEIGHT]=
{
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1},
  {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1},
  {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (action == GLFW_PRESS || action == GLFW_REPEAT) {
        switch (key) {
            case GLFW_KEY_A:

                break;

            case GLFW_KEY_D:

                break;

            case GLFW_KEY_W:

                break;
        }
    }
}

void draw() {
    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_QUADS);
    glVertex2f(boxPositionX, boxPositionY);
    glVertex2f(boxPositionX + boxSize, boxPositionY);
    glVertex2f(boxPositionX + boxSize, boxPositionY + boxSize);
    glVertex2f(boxPositionX, boxPositionY + boxSize);
    glEnd();
}

void update() {
for(int x = 0; x < MAPWIDTH * MAPHEIGHT; x++){
      double cameraX = 2 * x / MAPWIDTH - 1;
      double rayDirX = dirX + planeX * cameraX;
      double rayDirY = dirY + planeY * cameraX;

      int mapX = posX;
      int mapY = posY;
      double sideDistX;
      double sideDistY;

       //length of ray from one x or y-side to next x or y-side
      double deltaDistX = (rayDirX == 0);
      double deltaDistY = (rayDirY == 0);
      if (deltaDistX == 0){deltaDistX=1e30;}
      if (deltaDistY == 0){deltaDistY=1e30;}

      double perpWallDist;

      //what direction to step in x or y-direction (either +1 or -1)
      int stepX;
      int stepY;

      int hit = 0; //was there a wall hit?
      int side; //was a NS or a EW wall hit?
if (rayDirX < 0)
      {
        stepX = -1;
        sideDistX = (posX - mapX) * deltaDistX;
      }
      else
      {
        stepX = 1;
        sideDistX = (mapX + 1.0 - posX) * deltaDistX;
      }
      if (rayDirY < 0)
      {
        stepY = -1;
        sideDistY = (posY - mapY) * deltaDistY;
      }
      else
      {
        stepY = 1;
        sideDistY = (mapY + 1.0 - posY) * deltaDistY;
      }

        while (hit == 0)
      {
        //jump to next map square, either in x-direction, or in y-direction
        if (sideDistX < sideDistY)
        {
          sideDistX += deltaDistX;
          mapX += stepX;
          side = 0;
        }
        else
        {
          sideDistY += deltaDistY;
          mapY += stepY;
          side = 1;
        }
        //Check if ray has hit a wall
        if (worldMap[mapX][mapY] > 0) hit = 1;

        if(side == 0) perpWallDist = (sideDistX - deltaDistX);
        }

        int lineHeight = SCREENHEIGHT / perpWallDist;
        int drawStart = -lineHeight / 2 + SCREENHEIGHT / 2;
        if(drawStart < 0)drawStart = 0;
        int drawEnd = lineHeight / 2 + SCREENHEIGHT / 2;
        if(drawEnd >= SCREENHEIGHT)drawEnd = SCREENHEIGHT - 1;
        glBegin(GL_LINES);
        glVertex2f(drawStart, drawEnd); // Start point
        glEnd();
        }
}

int main() {

if (!glfwInit()) {
        fprintf(stderr, "Failed to initialize GLFW\n");
        return -1;
    }

    GLFWwindow* window = glfwCreateWindow(SCREENWIDTH, SCREENHEIGHT, "Box with Jumps Everywhere", NULL, NULL);
    if (!window) {
        fprintf(stderr, "Failed to create GLFW window\n");
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, keyCallback);
    glViewport(0, 0, SCREENWIDTH, SCREENHEIGHT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, SCREENWIDTH, SCREENHEIGHT, 0, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

      while (!glfwWindowShouldClose(window)) {
        update();

        glfwSwapBuffers(window);
        glfwPollEvents();
        }

}

错误排查与修复

核心距离计算问题

  1. deltaDistX/Y 计算完全错误
    原代码中double deltaDistX = (rayDirX == 0);用布尔值赋值,导致deltaDistX/Y只能是0或1,完全不符合射线步进逻辑。正确的计算应该是射线在x/y方向上,每移动一个地图格子的长度增量:

    double deltaDistX = fabs(1 / rayDirX);
    double deltaDistY = fabs(1 / rayDirY);
    

    当rayDirX/Y为0时,结果为无穷大,无需额外处理,此时射线不会在对应方向步进。

  2. perpWallDist 计算不完整且逻辑错误
    原代码仅在side=0时计算距离,且未考虑射线方向,无法避免鱼眼效应。正确的垂直距离计算应该是:

    if(side == 0) 
        perpWallDist = (mapX - posX + (1 - stepX)/2) / rayDirX;
    else 
        perpWallDist = (mapY - posY + (1 - stepY)/2) / rayDirY;
    

    或者基于sideDist和deltaDist计算:

    if(side == 0)
        perpWallDist = (sideDistX - deltaDistX) / rayDirX;
    else
        perpWallDist = (sideDistY - deltaDistY) / rayDirY;
    

其他关键错误

  1. 射线循环范围错误
    原代码for(int x = 0; x < MAPWIDTH * MAPHEIGHT; x++)循环次数为576次,但Raycaster需要为屏幕每一列发射一条射线,应改为:

    for(int x = 0; x < SCREENWIDTH; x++)
    
  2. 画线函数参数缺失
    glVertex2f(drawStart, drawEnd)仅提供一个顶点,GL_LINES需要两个顶点才能绘制线段。正确的做法是绘制当前列的垂直线:

    glBegin(GL_LINES);
    glVertex2f(x, drawStart); // 当前列顶部
    glVertex2f(x, drawEnd);   // 当前列底部
    glEnd();
    

    同时需在update函数开头添加glClear(GL_COLOR_BUFFER_BIT),避免画面叠加混乱。

  3. cameraX 计算错误
    原代码用MAPWIDTH作为分母,导致cameraX范围错误,应改为基于屏幕宽度计算:

    double cameraX = 2.0 * x / SCREENWIDTH - 1.0;
    
  4. 键盘控制未实现
    keyCallback中W/A/D按键逻辑为空,需补充移动和旋转逻辑(以W键移动为例):

    case GLFW_KEY_W:
        if(worldMap[(int)(posX + dirX * 0.1)][(int)posY] == 0) posX += dirX * 0.1;
        if(worldMap[(int)posX][(int)(posY + dirY * 0.1)] == 0) posY += dirY * 0.1;
        break;
    
  5. 时间变量未使用
    代码中定义的time和oldTime未用于控制帧率一致性,需在main循环中计算帧时间:

    while (!glfwWindowShouldClose(window)) {
        time = glfwGetTime();
        double frameTime = time - oldTime;
        oldTime = time;
    
        update(frameTime); // 传入帧时间控制移动步长
    
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    

内容的提问来源于stack exchange,提问作者Тимур Сенников

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:20:27