МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ ЛУГАНСКОЙ НАРОДНОЙ РЕСПУБЛИКИ ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ ЛУГАНСКОЙ НАРОДНОЙ РЕСПУБЛИКИ «ЛУГАНСКИЙ НАЦИОНАЛЬНЫЙ УНИВЕРСИТЕТ ИМЕНИ ВЛАДИМИРА ДАЛЯ» Факультет компьютерных систем и информационных технологий Кафедра информатики и программной инженерии Индивидуальная работа _____________Моделирование и анимация 3D объекта “Шкаф”____________ ______________________________________________________________ ______________________________________________________________ (название темы индивидуальной работы) Студент-исполнитель Приняли: Оценка: Матвиенко В.В.____________________ (Ф.И.О., подпись) 2-й курс, ИТ-692___________________ (курс, группа) Ромашова О. Н.__________________ (Ф.И.О., подпись) ЧигринаА. Н.______________________ (Ф.И.О., подпись) __________________________________ Луганск 2020 2 МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ ЛУГАНСКОЙ НАРОДНОЙ РЕСПУБЛИКИ ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ ЛУГАНСКОЙ НАРОДНОЙ РЕСПУБЛИКИ «ЛУГАНСКИЙ НАЦИОНАЛЬНЫЙ УНИВЕРСИТЕТ ИМЕНИ ВЛАДИМИРА ДАЛЯ» Факультет компьютерных систем и информационных технологий Кафедра информатики и программной инженерии Направление подготовки: 09.03.04 – Программная инженерия (шифр и название) ЗАДАНИЕ НА ИНДИВИДУАЛЬНУЮ РАБОТУ студенту Матвиенко Виталию Валерьевичу 1. Тема работы: «Моделирование и анимация 3D объекта в WebGL»; 2. Руководитель работы: Ромашова Ольга Николаевна, старший преподаватель; 3. Срок представления студентом работы: 25.12.2020; 4. Содержание индивидуальной работы: а) Реферат б) Основной текст в) Список источников г) Приложения 5. Дата выдачи задания: 02.12.2020. 3 ЗАДАНИЕ ДЛЯ ИНДИВИДУАЛЬНОЙ РАБОТЫ Прорисовка, текстурирование и анимация 3D модели «Шкаф» на WebGL. Программа должна иметь интерфейс со следующими возможностями: 1) Предоставлять пользователю возможность менять положение источника света (по осям); 2) изменять интенсивность освещенности по нажатию на кнопку (использовать выпадающий список с возможностью изменения мощности освещения в процентах); 3) вращать объект вокруг осей (с возможностью выбора вида оси OX, OY, OZ); 4) масштабировать объект (выбор масштаба с помощью кнопки из выпадающего списка); 5) шагом. перемещать объект на заданное расстояние по осям с выбранным 4 РЕФЕРАТ Работа содержит: 6 страниц основного текста, 11 страниц приложений, 6 рисунков, 3 использованный источник. Работа состоит из трех разделов, введения, выводов и приложений. Объект исследования: язык WebGL и прилежащих к ней библиотек. Методы исследования: изучение лекции и методических указаний. Целью работы: является прорисовка, текстурирование и анимация 3D модели «Шкаф» на языке WebGL. Результаты исследования: в процессе работы был смоделирован объект “Шкаф” и добавлены возможности такие как, менять положение источника света (по осям); изменять интенсивность освещенности по нажатию на кнопку; вращать объект вокруг осей; масштабировать объект; перемещать объект на заданное расстояние по осям с выбранным шагом. Ключевые слова: WEBGL, ТЕКСТУРИРОВАНИЕ, ПРОРИСОВКА. 5 СОДЕРЖАНИЕ ВВЕДЕНИЕ............................................................................................................. 6 РАЗДЕЛ 1. СОЗДАНИЕ ТРЕХМЕРНОЙ МОДЕЛИ ШКАФ ....................... 7 1.1 ДЕТАЛИ ДЛЯ ОБЪЕКТА ШКАФ .............................................................. 7 РАЗДЕЛ 2. СОЗДАНИЕ ОСВЕЩЕНИЯ И АНИМАЦИИ ОБЪЕКТА ....... 8 1.1 ОСВЕЩЕНИЕ ............................................................................................... 8 1.2 АНИМАЦИЯ ................................................................................................. 9 СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ ..................................... 12 ПРИЛОЖЕНИЯ .................................................................................................. 13 6 ВВЕДЕНИЕ WebGL является 3D графической библиотекой, которая позволяет современным интернет-браузерам отрисовывать 3D-сцены стандартным и эффективным способом. WebGL имеет клиенто-ориентированный подход; элементы, которые составляют части 3D-сцены, обычно загружаются с сервера. Однако, вся дальнейшая обработка, необходимая для получения изображения выполняется локально, с помощью графического оборудования клиента. В проекте будет рассмотрена задача моделирования различных объектов, добавление анимации и освещения. 7 РАЗДЕЛ 1 СОЗДАНИЕ ТРЕХМЕРНОЙ МОДЕЛИ ШКАФ 1.1 ДЕТАЛИ ДЛЯ ОБЪЕКТА ШКАФ Данная модель будет создаваться с помощью функций библиотеки Three.js, которая уменьшает количество строк при написании кода. Для того, чтобы получить модель шкафа нужно создать отдельные детали. Т.е. создать полки, боковые стороны, а также верхнюю, нижнюю и заднюю сторону шкафа. (Рис. 1.1). Рис.1.1 - Модель шкафа с его сторонами и полками (без текстуры и дверей) Рис. 1.2 - Каркасная модель шкафа 8 Рис. 1.3 – Модель с текстурой Создать геометрию куба можно с помощью функции THREE.BoxGeometry(). В скобках задаем значения (x,y,z). var cubeGeometry = new THREE.BoxGeometry (43,49,0); После чего используем функцию THREE.MeshPhongMaterial(), в которой устанавливаем материал и цвет (или текстуру) объекта. var cubeMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); Объединяем объект и материал с текстурой, функцией THREE.Mesh(). Проделываем данные шаги для каждой детали. После чего детали соединяем в один объект. И добавляем в сцену (Полный код JavaScript документа в Приложении А.1). 9 РАЗДЕЛ 2 СОЗДАНИЕ ОСВЕЩЕНИЯ И АНИМАЦИИ ОБЪЕКТА 1.1 ОСВЕЩЕНИЕ Освещение в библиотеке Three.js создается с помощью функции THREE.PointLight(): light = new THREE.PointLight( 0xFFFF00,1); Первым значением устанавливаем цвет в (hex формате), а вторым интенсивность света. 1.2 АНИМАЦИЯ Для того, чтобы анимировать наш объект, требуется создать функции, которые будут управлять моделью. В HTML коде напишем тег <input> и зададим такие параметры (Полный код HTML в Приложении Б.1): <input type="range" min="-150" max="150" id="line" oninput="testMy()" value="0"> Атрибут oninput это название нашей функции, которая будет использоваться в документе JavaScript. function testMy() { //Ползунок вращения по оси X line = document.getElementById("line").value; shkaf.rotation.x = line*0.02;} 10 Используя переменную line (в которой будет храниться значение ползунка) и функцию rotation.x, пользователь будет иметь возможность вращать объект по оси X (Рис. 2.1). Рис. 2.1 - Вращение по всем осям (OX,OY.OZ) Таким образом создаются функции для передвижения освещения (Рис.2.2), Рис. 2.2 - Предоставить пользователю возможность менять положение источника света (по осям); 11 Функция передвижения объекта “Шкаф” (Рис. 2.3), Рис. 2.3 - Перемещение объекта на заданное расстояние, по осям с выбранным шагом. Функция приближения (Рис. 2.4) Рис. 2.4 - Приближение объекта с помощью выпадающего списка 12 Функция интенсивности света (Рис. 2.5). Рис. 2.5 - Изменение интенсивности освещения по нажатию на кнопку 13 СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ 1. WebGL: программирование трехмерной графики. / Пер. с англ. Киселев А. Н. – М.: ДМК Пресс, 2015. – 494 с.: ил. 2. "3D-МОДЕЛИРОВАНИЕ НА WEBGL С ПОМОЩЬЮ БИБ­ЛИОТЕКИ THREE.JS: Учебное пособие", Вильданов А.Н. 2014 г., 114 с. 3. Основы Three.js < threejsfundamentals.org/threejs/lessons/ru/> 14 ПРИЛОЖЕНИЯ 15 Приложение А (код из документа JavaScript) var scene, renderer, camera; var cubeback; var cameraCenter = new THREE.Vector3(); var cameraHorzLimit = 50; var cameraVertLimit = 10; var mouse = new THREE.Vector2(); // ==-=-=-=-=-=-=-=-=-=-=-=-=-SHKAF-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= var shkaf = new THREE.Object3D(); //=============================Функции передвижения============================= function testMy() { //Ползунок изменения положения по X line = document.getElementById("line").value; shkaf.rotation.x = line*0.02; } function test1My(){//Ползунок изменения положения по Y line1 = document.getElementById("line1").value; shkaf.rotation.y = line1*0.02; } function test2My(){//Ползунок изменения положения по Y line1 = document.getElementById("line2").value; shkaf.position.z = line1; } //=========================Перемещение с шагом========================== function posMy() { //Ползунок изменения положения по X pos = document.getElementById("pos").value; shkaf.position.x = pos; } function pos1My(){//Ползунок изменения положения по Y pos1 = document.getElementById("pos1").value; shkaf.position.y = pos1; } function pos2My(){//Ползунок изменения положения по Y pos1 = document.getElementById("pos2").value; shkaf.position.z = pos1; } //=========================ZOOM========================== function zoom(){ zoom1 = document.getElementById("zoom_p").value; camera.position.z = zoom1; } function lightMy(){ inst = document.getElementById("light").value; light.position.x = inst; } function light1My(){ inst1 = document.getElementById("light1").value; light.position.y = inst1; } function light2My(){ inst2 = document.getElementById("light2").value; light.position.z = inst2; } function inten(){ intens = document.getElementById("intens").value; 16 return intens; } init(); animate(); function init() { renderer = new THREE.WebGLRenderer( {antialias:true} ); var width = window.innerWidth; var height = window.innerHeight; renderer.setSize (width, height); document.body.appendChild (renderer.domElement); //TEXTURE scene = new THREE.Scene(); //задняя сторона-----------------------------------------------------var cubeGeometry = new THREE.BoxGeometry (43,49,0); var cubeMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cubeback = new THREE.Mesh (cubeGeometry, cubeMaterial); cubeback.rotation.y = 3.14; cubeback.position.set (-7, 25, 0); //-------------------------------------------------------------------//left var cubeleft; var cubeleftGeom = new THREE.BoxGeometry (1,38.88,9); var cubeleftMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cubeleft = new THREE.Mesh (cubeleftGeom, cubeleftMaterial); cubeleft.position.set (-15, 20, 5); //-------------------------------------------------------------------//right var cuberight; var cuberightGeom = new THREE.BoxGeometry (-1,50,10); var cuberightMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cuberight = new THREE.Mesh (cuberightGeom, cuberightMaterial); cuberight.position.set (15, 25, 5); //left 2) var cubeleft2; var cubeleft2Geom = new THREE.BoxGeometry (-1,50,10); var cubeleft2Material = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cubeleft2 = new THREE.Mesh (cubeleft2Geom, cubeleft2Material); cubeleft2.position.set (-28.98, 25, 5); //top var cubeTop; var cubeTopGeom = new THREE.BoxGeometry (-45,1,10); var cubeTopMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cubeTop = new THREE.Mesh (cubeTopGeom, cubeTopMaterial); 17 cubeTop.position.set (-7, 50, 5); //-------------------------------------------------------------------//middlepalka var cubemiddle; var cubemiddleGeom = new THREE.BoxGeometry (1,50,9); var cubemiddleMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cubemiddle = new THREE.Mesh (cubemiddleGeom, cubeleftMaterial); cubemiddle.position.set (0, 25, 5); //-------------------------------------------------------------------//pol var cubePol; var cubePolGeom = new THREE.BoxGeometry (-45,1,10); var cubePolMaterial = new THREE.MeshPhongMaterial ({map: new THREE.TextureLoader().load('img/derevo.png')}); cubepol = new THREE.Mesh (cubePolGeom, cubePolMaterial); cubepol.position.set (-7, 0.1, 5); //------------------------------------------------------------//veshalka const Cyllgeometry = new THREE.CylinderBufferGeometry(0.5, 0.5, 14, 25); var CyllMaterial = new THREE.MeshPhongMaterial ({color: 0x808080}); cylinder = new THREE.Mesh (Cyllgeometry, CyllMaterial); cylinder.position.set (7, 45, 7); cylinder.rotation.z = 1.57; //polka1--------------------------------------------------------------var polka1; var polka1Geom = new THREE.BoxGeometry (-13.98,1,9); var polka1Material = new THREE.MeshPhongMaterial ({color: 0xA0522D}); polka1 = new THREE.Mesh (polka1Geom, polka1Material); polka1.position.set (-7.5, 10, 5); //polka2--------------------------------------------------------------var polka2; var polka2Geom = new THREE.BoxGeometry (-13.98,1,9); var polka2Material = new THREE.MeshPhongMaterial ({color: 0xA0522D}); polka2 = new THREE.Mesh (polka2Geom, polka2Material); polka2.position.set (-7.5, 20, 5); //polka3--------------------------------------------------------------var polka3; var polka3Geom = new THREE.BoxGeometry (-13.98,1,9); var polka3Material = new THREE.MeshPhongMaterial ({color: 0xA0522D}); polka3 = new THREE.Mesh (polka3Geom, polka3Material); polka3.position.set (-7.5, 30, 5); //bigpolka--------------------------------------------------------------var bigpolka; var bigpolkaGeom = new THREE.BoxGeometry (-27.98,1,9); var bigpolkaMaterial = new THREE.MeshPhongMaterial ({color: 0xA0522D}); bigpolka = new THREE.Mesh (bigpolkaGeom, bigpolkaMaterial); bigpolka.position.set (-14.5,40, 5); //light------------------------------------------------------------------var intena = inten(); light = new THREE.PointLight( 0xFFFF00,intena); scene.add( light ); // Geometry ===============================================================Doska black and White var cbgeometry = new THREE.PlaneGeometry( 500, 500, 8, 8 ); 18 // Materials var cbmaterials = []; cbmaterials.push( side: THREE.DoubleSide }) cbmaterials.push( side: THREE.DoubleSide }) new THREE.MeshPhongMaterial( { color: 0xffffff, ); new THREE.MeshPhongMaterial( { color: 0x000000, ); var l = cbgeometry.faces.length / 2; // <-- Right here. This should still be 8x8 (64) console.log("This should be 64: " + l);// Just for debugging puporses, make sure this is 64 for( var i = 0; i < l; i ++ ) { j = i * 2; // <-- Added this back so we can do every other 'face' cbgeometry.faces[ j ].materialIndex = ((i + Math.floor(i/8)) % 2); // The code here is changed, replacing all 'i's with 'j's. KEEP THE 8 cbgeometry.faces[ j + 1 ].materialIndex = ((i + Math.floor(i/8)) % 2); // Add this line in, the material index should stay the same, we're just doing the other half of the same face } // Mesh cb = new THREE.Mesh( cbgeometry, new THREE.MeshPhongMaterial( cbmaterials ) ); cb.position.x = -Math.PI/2; cb.position.y = -Math.PI/2; cb.rotation.set(300,0,0); scene.add( cb ) // Geometry ===============================================================Doska black and White //add to Object3D: shkaf.add(polka1,polka2,polka3,bigpolka); shkaf.add(cubepol); shkaf.add(cylinder); shkaf.add(cubemiddle); shkaf.add(cubeTop); shkaf.add(cubeback); shkaf.add(cubeleft,cuberight,cubeleft2); // add shkaf to scene scene.add(shkaf); shkaf.position.x = 0; camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000); camera.position.x = 0; camera.position.y = 60; camera.position.z = 250; camera.rotation.x = -0.2; //camera.lookAt (new THREE.Vector3(0,0,0)); renderer.setClearColor (0x87CEEB, 1); //87CEEB } function animate() { //updateCamera(); requestAnimationFrame ( animate ); renderer.render (scene, camera); } 19 Приложение Б (код из документа HTML) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>lab</title> <style type="text/css"> body { margin-top: 0; margin-left: 0; } .box { width: 100%; height: 90px; background-image: url("https://wallpaper-mania.com/wpcontent/uploads/2018/09/High_resolution_wallpaper_background_ID_77701893136.j pg"); color: #FFFFFF; } #my { margin-top: 0%; padding-top: 0px; margin-left: 5%; position: absolute; } #my1 { margin-top: 2%; padding-top: 0px; margin-left: 5%; position: absolute; } #my2 { margin-top: 4%; padding-top: 0px; margin-left: 5%; position: absolute; } #my3 { margin-left: 27%; margin-top: 0%; padding-top: 0px; position: absolute; } #my4 { margin-left: 45%; margin-top: 0%; padding-top: 0px; position: absolute; } #my5 { margin-left: 45%; margin-top: 2%; padding-top: 0px; position: absolute; } #my6 { margin-left: 45%; margin-top: 4%; padding-top: 0px; 20 position: absolute; } #my7 { margin-left: 27%; margin-top: 2%; padding-top: 0px; position: absolute; } #my8 { margin-left: 70%; margin-top: 0%; padding-top: 0px; position: absolute; } #my9 { margin-left: 70%; margin-top: 2%; padding-top: 0px; position: absolute; } #my10 { margin-left: 70%; margin-top:4%; padding-top: 0px; position: absolute; } </style> </head> <body> <div class="box"> <!-- Ползунки изменения !--> <div id="my"> <input type="range" min="-150" max="150" id="line" oninput="testMy()" value="0"> Повернуть шкаф по оси X</div> <div id="my1"> <input type="range" min="-150" max="150" id="line1" oninput="test1My()" value="0"> Повернуть шкаф по оси Y</div> <div id="my2"> <input type="range" min="-100" max="100" id="line2" oninput="test2My()" value="0"> Повернуть шкаф по оси Z</div> <!-- ---------------------------Выпадающий список-----------------------!--> <div id="my3">Приблизить объект на: <select name = "blacklist" oninput="zoom()" id="zoom_p"> <option value = "150" selected>0%</option> <option value = "100">50%</option> <option value = "50" >100%</option> </select></div> <!---------------------Ползунки изменения-----------------!--> <div id="my4"> <input type="range" min="-100" max="100" step="50" id="pos" oninput="posMy()" value="0"> Переместить шкаф по оси X</div> <div id="my5"> <input type="range" min="-20" max="20" step="10" id="pos1" oninput="pos1My()" value="0"> Переместить шкаф по оси Y</div> <div id="my6"> <input type="range" min="-100" max="100" step="50" id="pos2" oninput="pos2My()" value="0"> Переместить шкаф по оси Z</div> <!-- ---------------------------Освещение-----------------------!--> <div id="my8"> <input type="range" min="-500" max="500" id="light" 21 oninput="lightMy()" value="0"> Переместить свет по оси X</div> <div id="my9"> <input type="range" min="-500" max="500" oninput="light1My()" value="0"> Переместить свет по оси Y</div> <div id="my10"> <input type="range" min="-500" max="500" oninput="light2My()" value="0"> Переместить свет по оси Z</div> <div id="my7">Освещение на: <select name id="intens"> <option value = "0.8" selected>0%</option> <option value = "0.5">50%</option> <option value = "1" >100%</option> </select></div> </div> = id="light1" id="light2" "blacklist" <script type="text/javascript" src="three.js"></script> <script type="text/javascript" src="webgl.js"></script> </body> </html> oninput="inten()"