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;
}