2.5D Renderer

Stage 5: Текстурирование

Буфер

При работе с CanvasRenderingContext2D и многократных вызовах fillRect накапливаются накладные расходы.


  function drawPixel(
    ctx: CanvasRenderingContext2D,
    x: number,
    y: number,
    color: Color
  ): void {
    ctx.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b})`;
    ctx.fillRect(x, y, 1, 1);
  }
  

Лучше сделать одну дорогую операцию putImageData, чем миллион дешевых fillRect. Для этого создадим буфер и будем его передавать сквозь все функции вплоть до вызова drawPixel, а после вызовем putImageData.


  export function createRender25d({ bspTree }: { bspTree: BSPNode }) {
    return function render25d(
      ctx: CanvasRenderingContext2D,
      settings: Settings,
    ) {
      const camera = settings.camera;
      const buffer = ctx.createImageData(camera.screen.width, camera.screen.height);

      for (let i = 0; i < buffer.data.length; i += 4) {
        buffer.data[i] = 0
        buffer.data[i + 1] = 0
        buffer.data[i + 2] = 0
        buffer.data[i + 3] = 255
      }

      // ..
      traverseBSPTree(bspTree, camera, (bspNode: BSPLeaf) => {
        // ..
        if (isPortal(seg)) {
          drawPortalSegment(buffer, camera, seg, angles, wallRanges, upperClip, lowerClip);
        } else {
          drawSolidSegment(buffer, camera, seg, angles, wallRanges, upperClip, lowerClip);
        }
        // ..
      });

      ctx.putImageData(buffer, 0, 0);
    }
  }
    

Внутри drawPixel заполняем буфер данными о необходимом пикселе.


  function drawPixel(
    buffer: ImageData,
    x: number,
    y: number,
    color: Color
  ): void {
    const index = (y * buffer.width + x) * 4;

    buffer.data[index] = color.r;
    buffer.data[index + 1] = color.g;
    buffer.data[index + 2] = color.b;
    buffer.data[index + 3] = 255;
  }

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