Среда программирования:
Visual Studio Code
Статья по теме:
Программа демонстрирует построение поверхности, заданной параметрически, с удалением невидимых граней по методу плавающего горизонта и освещение по Ламберту.
Код программы:
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var canvasWidth = 800; var canvasHeight = 800; var pixelMap = []; // заполняем карту пикселей нулями for (var i = 0; i < 800; i += 1) { var subres = []; for (var j = 0; j < 800; j += 1) { subres.push(0); } pixelMap.push(subres); } const reducer = (previousValue, currentValue) => previousValue + currentValue; // складывает все значения в массиве let xPr, yPr; let xRotation, yRotation, zRotation; let xRotation_raw, yRotation_raw, zRotation_raw; function rotate(x, y, z, axis = 0, alpha = undefined) { if (axis == 0 || alpha === undefined) return [x, y, z]; else if (axis == 1) return [x, y * Math.cos(alpha) + z * Math.sin(alpha), -y * Math.sin(alpha) + z * Math.cos(alpha)]; else if (axis == 2) return [x * Math.cos(alpha) + z * Math.sin(alpha), y, -x * Math.sin(alpha) + z * Math.cos(alpha)]; else if (axis == 3) return [x * Math.cos(alpha) + y * Math.sin(alpha), -x * Math.sin(alpha) + y * Math.cos(alpha), z]; } function getProjectionXY(x, y, z, k, x_bias, y_bias) { return [(k * x) / (z + k) * 50 + x_bias, (k * y) / (z + k) * 50 + y_bias]; } // сборка массива пикселей в виде вертикальной линии function extractVertical(slice, ind) { let result = []; slice.forEach(elem => { result.push(elem[ind]); }); return result; } function draw(angle = 16) { //для наглядности: 4, 5, 10, 11!!, 16!!, 17, 18! var I_D, lx, ly, lz; // Координаты источника света и диффузное освещение //ctx.clearRect(0, 0, 800, 800); for (let x = -5; x < 5; x += 0.1) { [xRotation, yRotation, zRotation] = rotate(x, Math.sin(x) * Math.cos(x) / 0, -5, 2, angle); [xRotation, yRotation, zRotation] = rotate(xRotation, yRotation, zRotation, 1, 185); // отрисовка графика for (let z = 5; z > -5; z -= 0.02) { ctx.beginPath(); ctx.moveTo(...getProjectionXY(xRotation, yRotation, zRotation, 30, 400, 400)); lx = -1.8, ly = -15.7, lz = -3.707107; //освещение I_D = (xRotation * lx + yRotation * ly + zRotation * lz); ctx.strokeStyle = `rgba( 0, ${Math.floor(10*I_D)}, ${Math.floor(10*I_D)}, 255)`; // получаем проекцию точки на плоскость [xRotation, yRotation, zRotation] = rotate(x, Math.sin(x) * Math.cos(z), z, 2, angle); [xRotation, yRotation, zRotation] = rotate(xRotation, yRotation, zRotation, 1, 50); [xPr, yPr] = getProjectionXY(xRotation, yRotation, zRotation, 30, 400, 400); // округление для получения точного значения пикселей var xInd = Math.round(xPr); var yInd = Math.round(yPr); // получаем количество отрисованных пикселей с каждой стороны от планируемой точки leftSum = pixelMap[yInd].slice(0, xInd).reduce(reducer); rightSum = pixelMap[yInd].slice(xInd, 800).reduce(reducer); topSum = extractVertical(pixelMap.slice(0, yInd), xInd).reduce(reducer); bottomSum = extractVertical(pixelMap.slice(yInd, 800), xInd).reduce(reducer); if (leftSum && rightSum && topSum && bottomSum) { // пропускаем точку, если по всем сторонам что-то отрисовано ctx.moveTo(xPr, yPr); } else { // рисуем пиксель если хотя бы с одной стороны их нет ctx.lineTo(xPr, yPr); pixelMap[yInd][xInd] = 1; } ctx.stroke(); // вызов отрисовки ctx.closePath(); } } }
Прикрепленный файл | Размер |
---|---|
GrafSvet№8.rar | 2.34 кб |