2.5D Renderer

Stage 1: Отрисовка стен

Отсечение: учитываем угол камеры

В прошлом примере стены были расположены так, что всегда были повернуты лицом к камере. Если расположить стены по-другому, например, как на демо ниже и слева, то можно заметить, что ранее реализованная линейная интерполяция не работает.

Здесь необходимо учитывать угол между стеной и направлением взгляда, чтобы создать правильное перспективное искажение. Поэтому мы введем специальный коэффициент, scale factor, учитывающий ориентацию стены относительно луча зрения.

2.5D Renderer (not fixed)

2D Renderer

Управление камерой WASD

2.5D Renderer (fixed)

Как это рассчитать

Коэффициент, во сколько раз нужно изменить базовую высоту из-за угла обзора стены:


    function caclulateScaleFactor(
      screenX: number,
      linedef: Linedef,
      camera: Camera
    ): number {
      const distance = distanceToLinedef(linedef, camera);
      const screenXAngle = angleFromScreenX(screenX, camera);
    
      const wallDir = toAngle(linedef.end, linedef.start);
      const wallNormal = new Angle(wallDir.degrees + 90);
    
      const viewAngle = camera.angle.degrees + screenXAngle.degrees;
      const skewAngle = new Angle(viewAngle - wallNormal.degrees);
      const skewAngleCos = Math.abs(skewAngle.cos);
    
      const screenXAngleCos = Math.abs(screenXAngle.cos);
      
      return (distance * skewAngleCos) / (distance * screenXAngleCos);
    }

Обновим функцию расчета проекции:


  function projectLinedef(camera: Camera, linedef: Linedef) : LinedefProjection | null {
    const angles = calculateIntersectionAngles(linedef, camera);

    if (angles === null) {
      return null;
    }

    const distanceToCamera = distanceToLinedef(linedef, camera)

    const startScreenX = angles.linedefFrom < angles.cameraFrom 
      ? 0 
      : toScreenX(angles.linedefFrom, angles, camera);
      
    const endScreenX = angles.linedefTo > angles.cameraTo
      ? camera.screen.width 
      : toScreenX(angles.linedefTo, angles, camera);

    const startScale = caclulateScaleFactor(startScreenX, linedef, camera);
    const endScale = caclulateScaleFactor(endScreenX, linedef, camera);

    return {
      start: {
        screenX: startScreenX,
        height: WALL_HEIGHT * startScale / distanceToCamera
      },
      end: {
        screenX: endScreenX,
        height: WALL_HEIGHT * endScale / distanceToCamera
      }
    };
  }

Реализация шага на github