3D моделирование объектов с помощью HTML5
Сегодняшний урок является мостом между двухмерной графики в HTML5 и по-настоящему трехмерными (с помощью WebGL). Сегодня я покажу, как нарисовать трехмерные объекты с помощью полигональной сетки. Полигон сетки или неструктурированные сетки представляет собой набор вершин, ребер и граней, который определяет форму многогранных объектов в 3D компьютерной графики и твердотельного моделирования. Лица, обычно, состоят из треугольников, четырехугольников или других простых выпуклых многоугольников, так как это упрощает рендеринг.
Много различным бесплатных шаблонов вы можете найти на сайте наших партнеров, также имеются и премиум шаблоны с круглосуточной техподдержкой и помощью в настройке:
Для того, чтобы понять, о чем идет речь, я рекомендую прочитать основе описанных в Википедии . Чтобы продемонстрировать это, мы подготовили простые трехмерные объекты — куб и многомерной сферы (с переменным числом граней). И так, давайте приступим.
Шаг 1. HTML
Как обычно (для всех примеров для демо) у нас есть очень простая HTML-разметка (с одного объекта внутри canvas):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<html lang="en" > <head> <meta charset="utf-8" /> <meta name="author" content="Script Tutorials" /> <!-- Стили --> <link href="css/main.css" rel="stylesheet" type="text/css" /> <!-- script --> <script src="js/meshes.js"></script> <script src="js/transform.js"></script> <script> //var obj = new cube(); //var obj = new sphere(6); var obj = new sphere(16); </script> <script src="js/main.js"></script> </head> <body> <div> <canvas id="scene" height="500" width="700" tabindex="1"></canvas> </div> </body> </html> |
Теперь нам необходимо инициализировать сам плагин:
1 2 3 4 5 |
<script> //var obj = new cube(); //var obj = new sphere(6); var obj = new sphere(16); </script> |
Это означает, что если нам нужно вывести куб — вы должны отобразить это в первой линии, если вы хотите, отобразить сферу с 6 граней — вам нужно выбрать второй вариант.
Шаг 2. JavaScript
Есть три JS файла (main.js, meshes.js и transform.js), мы публикуем два из них, третий (transform.js) содержит только математику функций (для вращения, масштабирования, перевод и объектов проекта) . Она будет доступна в исходниках. Итак, давайте рассмотрим код первого JavaScript:
meshes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
// Получить случайный цвет function getRandomColor() { var letters = '0123456789ABCDEF'.split(''); var color = '#'; for (var i = 0; i < 6; i++ ) { color += letters[Math.round(Math.random() * 15)]; } return color; } // Подготовим объект function prepareObject(o) { o.colors = new Array(); o.normals = new Array(); for (var i = 0; i < o.faces.length; i++) { o.normals[i] = [0, 0, 0]; o.colors[i] = getRandomColor(); } // Рассчитываем максимальную позицию o.center = [0, 0, 0]; for (var i = 0; i < o.points.length; i++) { o.center[0] += o.points[i][0]; o.center[1] += o.points[i][1]; o.center[2] += o.points[i][2]; } // Подготовим расстояния o.distances = new Array(); for (var i = 1; i < o.points.length; i++) { o.distances[i] = 0; } // Вычисляем среднее положение центра o.points_number = o.points.length; o.center[0] = o.center[0] / (o.points_number - 1); o.center[1] = o.center[1] / (o.points_number - 1); o.center[2] = o.center[2] / (o.points_number - 1); o.faces_number = o.faces.length; o.axis_x = [1, 0, 0]; o.axis_y = [0, 1, 0]; o.axis_z = [0, 0, 1]; } // Cube объект function cube() { // Подготовим точки и грани для куба this.points=[ [0,0,0], [100,0,0], [100,100,0], [0,100,0], [0,0,100], [100,0,100], [100,100,100], [0,100,100], [50,50,100], [50,50,0], ]; this.faces=[ [0,4,5], [0,5,1], [1,5,6], [1,6,2], [2,6,7], [2,7,3], [3,7,4], [3,4,0], [8,5,4], [8,6,5], [8,7,6], [8,4,7], [9,5,4], [9,6,5], [9,7,6], [9,4,7], ]; prepareObject(this); } // Сфера объекта function sphere(n) { var delta_angle = 2 * Math.PI / n; // Подготовим вершины (точеки) сферы var vertices = []; for (var j = 0; j < n / 2 - 1; j++) { for (var i = 0; i < n; i++) { vertices[j * n + i] = []; vertices[j * n + i][0] = 100 * Math.sin((j + 1) * delta_angle) * Math.cos(i * delta_angle); vertices[j * n + i][1] = 100 * Math.cos((j + 1) * delta_angle); vertices[j * n + i][2] = 100 * Math.sin((j + 1) * delta_angle) * Math.sin(i * delta_angle); } } vertices[(n / 2 - 1) * n] = []; vertices[(n / 2 - 1) * n + 1] = []; vertices[(n / 2 - 1) * n][0] = 0; vertices[(n / 2 - 1) * n][1] = 100; vertices[(n / 2 - 1) * n][2] = 0; vertices[(n / 2 - 1) * n + 1][0] = 0; vertices[(n / 2 - 1) * n + 1][1] = -100; vertices[(n / 2 - 1) * n + 1][2] = 0; this.points = vertices; // Подготовим плоскости var faces = []; for (var j = 0; j < n / 2 - 2; j++) { for (var i = 0; i < n - 1; i++) { faces[j * 2 * n + i] = []; faces[j * 2 * n + i + n] = []; faces[j * 2 * n + i][0] = j * n + i; faces[j * 2 * n + i][1] = j * n + i + 1; faces[j * 2 * n + i][2] = (j + 1) * n + i + 1; faces[j * 2 * n + i + n][0] = j * n + i; faces[j * 2 * n + i + n][1] = (j + 1) * n + i + 1; faces[j * 2 * n + i + n][2] = (j + 1) * n + i; } faces[j * 2 * n + n - 1] = []; faces[2 * n * (j + 1) - 1] = []; faces[j * 2 * n + n - 1 ][0] = (j + 1) * n - 1; faces[j * 2 * n + n - 1 ][1] = (j + 1) * n; faces[j * 2 * n + n - 1 ][2] = j * n; faces[2 * n * (j + 1) - 1][0] = (j + 1) * n - 1; faces[2 * n * (j + 1) - 1][1] = j * n + n; faces[2 * n * (j + 1) - 1][2] = (j + 2) * n - 1; } for (var i = 0; i < n - 1; i++) { faces[n * (n - 4) + i] = []; faces[n * (n - 3) + i] = []; faces[n * (n - 4) + i][0] = (n / 2 - 1) * n; faces[n * (n - 4) + i][1] = i; faces[n * (n - 4) + i][2] = i + 1; faces[n * (n - 3) + i][0] = (n / 2 - 1) * n + 1; faces[n * (n - 3) + i][1] = (n / 2 - 2) * n + i + 1; faces[n * (n - 3) + i][2] = (n / 2 - 2) * n + i; } faces[n * (n - 3) - 1] = []; faces[n * (n - 2) - 1] = []; faces[n * (n - 3) - 1][0] = (n / 2 - 1) * n; faces[n * (n - 3) - 1][1] = n - 1; faces[n * (n - 3) - 1][2] = 0; faces[n * (n - 2) - 1][0] = (n / 2 - 1) * n + 1; faces[n * (n - 2) - 1][1] = (n / 2 - 2) * n; faces[n * (n - 2) - 1][2] = (n / 2 - 2) * n + n - 1; this.faces=faces; prepareObject(this); } |
В самом начале, мы должны подготовить все точки и плоскости наших объектов. Есть 2 функции: куб (который генерирует начальные массивы для простого объекта куба) и сферы (для генерации сфер). Как вы видите, — это гораздо сложнее вычислить все точки и плоскости для многомерной сферы. Как только мы получим все эти точки и поверхности, мы должны вычислить другие параметрами (например, нормалей, расстояний, абсолютный центр и три оси).
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
// Внутренние переменные var canvas, ctx; var vAlpha = 0.5; var vShiftX = vShiftY = 0; var distance = -700; var vMouseSens = 0.05; var iHalfX, iHalfY; // Инициализация function sceneInit() { // Подготовим объекты canvas = document.getElementById('scene'); ctx = canvas.getContext('2d'); iHalfX = canvas.width / 2; iHalfY = canvas.height / 2; // Начальный масштаб и перевод scaleObj([3, 3, 3], obj); translateObj([-obj.center[0], -obj.center[1], -obj.center[2]],obj); translateObj([0, 0, -1000], obj); // Присоединить обработчики событий document.onkeydown = handleKeydown; canvas.onmousemove = handleMousemove; // Основной цикл сцен setInterval(drawScene, 25); } // Обработчик события OnKeyDown function handleKeydown(e) { kCode = ((e.which) || (e.keyCode)); switch (kCode) { case 38: vAlpha = (vAlpha <= 0.9) ? (vAlpha + 0.1) : vAlpha; break; // Up key case 40: vAlpha = (vAlpha >= 0.2) ? (vAlpha - 0.1) : vAlpha; break; // Down key } } // Обработчик события OnMouseMove function handleMousemove(e) { var x = e.pageX - canvas.offsetLeft; var y = e.pageY - canvas.offsetTop; if ((x > 0) && (x < canvas.width) && (y > 0) && (y < canvas.height)) { vShiftY = vMouseSens * (x - iHalfX) / iHalfX; vShiftX = vMouseSens * (y - iHalfY) / iHalfY; } } // Рисуем основную функцию сцену function drawScene() { // Очистка полотна ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Установить цвет заливки, цвет обводки, толщину линии. ctx.strokeStyle = 'rgb(0,0,0)'; ctx.lineWidth = 0.5; ctx.globalAlpha= vAlpha; // Вертикальное и горизонтальное вращение var vP1x = getRotationPar([0, 0, -1000], [1, 0, 0], vShiftX); var vP2x = getRotationPar([0, 0, 0], [1, 0, 0], vShiftX); var vP1y = getRotationPar([0, 0, -1000], [0, 1, 0], vShiftY); var vP2y = getRotationPar([0, 0, 0], [0, 1, 0], vShiftY); rotateObj(vP1x, vP2x, obj); rotateObj(vP1y, vP2y, obj); // Пересчитываем расстояния for (var i = 0; i < obj.points_number; i++) { obj.distances[i] = Math.pow(obj.points[i][0],2) + Math.pow(obj.points[i][1],2) + Math.pow(obj.points[i][2], 2); } // Подготовить массив с плоскостью треугольников (с расчетом максимальное расстояние для каждой грани) var iCnt = 0; var aFaceTriangles = new Array(); for (var i = 0; i < obj.faces_number; i++) { var max = obj.distances[obj.faces[i][0]]; for (var f = 1; f < obj.faces[i].length; f++) { if (obj.distances[obj.faces[i][f]] > max) max = obj.distances[obj.faces[i][f]]; } aFaceTriangles[iCnt++] = {faceVertex:obj.faces[i], faceColor:obj.colors[i], distance:max}; } aFaceTriangles.sort(sortByDistance); // Подготовка массива с прогнозируемыми пунктами var aPrjPoints = new Array(); for (var i = 0; i < obj.points.length; i++) { aPrjPoints[i] = project(distance, obj.points[i], iHalfX, iHalfY); } // Нарисовать объект for (var i = 0; i < iCnt; i++) { ctx.fillStyle = aFaceTriangles[i].faceColor; // Начинаем путь ctx.beginPath(); // Плоскость индекса вершины var iFaceVertex = aFaceTriangles[i].faceVertex; // Переместить в исходное положение ctx.moveTo(aPrjPoints[iFaceVertex[0]][0], aPrjPoints[iFaceVertex[0]][1]); // И нарисовать три линии (построить треугольник) for (var z = 1; z < aFaceTriangles[i].faceVertex.length; z++) { ctx.lineTo(aPrjPoints[iFaceVertex[z]][0], aPrjPoints[iFaceVertex[z]][1]); } // Закрыть путь ctx.closePath(); ctx.stroke(); ctx.fill(); } } // Функция сортировки function sortByDistance(x, y) { return (y.distance - x.distance); } // Инициализация if (window.attachEvent) { window.attachEvent('onload', sceneInit); } else { if (window.onload) { var curronload = window.onload; var newonload = function() { curronload(); sceneInit(); }; window.onload = newonload; } else { window.onload = sceneInit; } } |
Вот и все. Готово!
Материал взят из зарубежного источника. И представлен исключительно в ознакомительных целях.
Читайте также:
Опубликовал Cooper 01.03.2013 в 22:30, в категории Веб-дизайн. Вы можете следить за комментариями через RSS 2.0. Вы можете перейти в конец записи и оставить комментарий. Пинги запрещены. |