WebGL — це 3D для Інтернету. Як випливає із назви, це пов'язано з OpenGL, стандартним промисловим API для апаратного прискорення 3D-графіки. 3D є набагато складніше 2D. Ми маємо справу не тільки з повною системою тривимірних координат і пов'язаною з цим математикою, але також маємо більше турбуватися про стан графічного контексту. І це набагато, набагато більше, ніж основні кольори та трансформація двовимірного контексту.
У 2D ми малюємо фігури з контурами, потім заливаємо кольором. Це дуже просто. З іншого боку, 3D включає досить складний багатоступінчастий процес.
Для початку в тривимірному просторі у нас є фігури у вигляді геометрії, списку точок званих «вектори». Далі нам, можливо, буде потрібна додаткова інформація для фігури. Наприклад, нормаль до поверхні визначає напрямок, у якому світло відбивається від постаті. Потім, ми повинні налаштувати джерела світла та камери. Камера визначає точку зору. Світло це точка у просторі, яка визначає, звідки йде світло. Після налаштування всього цього застосовуємо шейдери.
Шейдери беруть камеру, світло, нормалі та геометрію як вхідні дані та перетворюють їх на фактичні пікселі. Я розумію, що це дуже спрощене пояснення OpenGL, але будь ласка, потерпіть. Існують два види шейдерів, один використовується для модифікації векторів, щоб створити остаточні відображення світла, а інший малює фактичні пікселі. Останнє шейдери зі зрозумілих причин відомі як піксельні шейдери.
Шейдери по суті це крихітні програми, написані спеціальною мовою OpenGL, яка виглядає як різновид Сі. Цей код не дуже легко писати, тому що він має бути з масовим паралелізмом. Сучасний графічний процесор, по суті, це спеціальний супер паралельний багатоядерний чіп, який робить одну річ дуже ефективно: відображає безліч пікселів дуже швидко. Шейдери це потужність сучасної графіки, але з ними не так легко працювати. Позитивною стороною вашої програми є встановлення своїх власних шейдерів для створення безлічі дивовижних речей, але це ж і мінус. Немає шейдерів, вбудованих у стандарт WebGL. Ви повинні принести свої власні.
Вище наведена спрощена версія, як працює OpenGL ES 2.0 та OpenGL 3 (старі версії OpenGL не містять шейдерів). Це складна, але гнучка система. WebGL є по суті тим самим, але з JavaScript API замість Сі.
У нас просто немає часу, щоб навчити вас OpenGL. Ми могли б легко заповнити весь тиждень навчанням OpenGL. Навіть якби ми мали час, ви, ймовірно, не буде писати код таким чином. Це займе тисячі рядків коду для створення досить простої гри. Натомість ви повинні використовувати бібліотеку або графічний двигун, щоб робити низькорівневі штуки за вас, дозволяючи насправді сконцентруватися на вашому додатку. У світі найпопулярніша WebGL-бібліотека називається ThreeJS і є проектом з відкритим вихідним кодом. Вона значно спрощує побудову інтерактивних тривимірних програм і поставляється з власним набором багаторазових шейдерів. Ось те, чому я збираюся навчити вас сьогодні: ThreeJS.
Приклади
Спочатку кілька прикладів.
Ця проста гра називається Зомбі проти Корів, де ви використовуєте стрілки, щоб не дати зомбі з'їсти корів. Це тривимірна гра з апаратним прискоренням. Вона виглядає як професійна гра, яку ви можете побачити на Wii, але запускається виключно в браузері.
Ось ще один приклад, Google Earth, без встановлення окремої програми.
Ось ще один цікавий приклад візуалізації аудіо в 3D.
Всі вони були створені на TreeJS і WebGL.
Підтримка браузерами
Перш ніж ми підемо далі, кілька слів про підтримку браузерами. Opera, Firefox та всі десктопні браузери на основі WebKit підтримують WebGL. Великою дірою є Internet Explorer. IE 10 чудово підтримує двовимірне полотно, але не підтримує WebGL. Крім того, Microsoft не анонсувала свої плани щодо його підтримки в майбутньому.
На мобільних пристроях практично немає підтримки WebGL. iOS підтримує, але лише як частину iAd, а не у звичайному браузері. Це, однак, говорить про те, що Apple, можливо, додасть його у майбутньому. Деякі телефони на Android підтримують WebGL, але, як правило, лише якщо встановлений альтернативний браузер, як Firefox або Opera. Оскільки десктопний Chrome підтримує WebGL, а Google робить Chrome на Android за замовчуванням, сподіваємось, що отримаємо WebGL як стандарт Android. Єдиний реальний мобільний пристрій, який постачається з гарною підтримкою WebGL із коробки — BlackBerry PlayBook. Таким чином, хоча підтримка для мобільних пристроїв не краща, ймовірно, вона покращиться в наступному році або трохи пізніше. WebGL стане частиною майбутніх веб-стандартів і за ним стоять деякі великі імена, так що тепер потрібний час для старту. програмістом Mr. Doob. Його справжнє ім'я Рікардо Кабельо, але якщо ви шукаєте Mr. Doob, знайдете його класні графічні хакі за останнє десятиліття. ThreeJS є бібліотекою, яка знаходиться на вершині WebGL. Вона автоматизує дратівливих речей, так що ви можете зосередитися на вашому додатку. Щоб зробити це ще простіше попрацюйте з Джером Етьєн, він створив BoilerPlate Builder, який дозволить вам швидко почати. Всі основні речі, на зразок камери, управління мишею і візуалізація вже додаються, так що ви можете відразу почати працювати з додатком ThreeJS. Шаблон містить кілька налаштувань, але для наших проектів ви можете просто залишити їх за замовчуванням. Перейдемо до BoilerPlate Builder та завантажуємо новий шаблон. Розпакуйте його та відкрийте сторінку index.html у вашому браузері, щоб переконатися, що все працює. Ви повинні побачити щось на зразок цього:
Тепер Відкрийте файл index.html у текстовому редакторі. Зауважте, що шаблон дуже добре документований. Почнімо з функції init.
<code data-language="javascript">//ініціалізація сцени function init(){ if( Detector.webgl) { renderer=new THREE.WebGLRenderer({ antialias : true, //більш згладжений результат preserveDrawingBuffer : true //дозволяє скріншоти }); renderer.setClearColorHex( 0xBBBBBB, 1); //Розкоментуйте, якщо потрібно WebGL //} else { //Detector.addGetWebGLMessage (); //return true; }else{ renderer=new THREE.CanvasRenderer(); } renderer.setSize( window.innerWidth, window.innerHeight); document.getElementById('container').appendChild(renderer.domElement);
Спочатку шаблон ініціалізує систему. Вона намагається створити візуалізацію WebGL, тому що насправді ThreeJS підтримує деякі інші движки, такі як 2D Canvas. У цьому прикладі ми хочемо WebGL. Якщо вона не може створити WebGLRenderer, то повернеться назад до 2D Canvas. Хоча Canvas працює набагато повільніше, це може бути краще ніж нічого. Вам вирішувати.
Потому встановлюється розмір полотна і додається на сторінку як дитина контейнера (<div> оголошено в документі).
<code data-language="javascript">//додайте Stats.js — https://github.com/mrdoob/stats.js stats=new Stats(); stats.domElement.style.position='absolute'; stats.domElement.style.bottom='0px'; document.body.appendChild( stats.domElement);
Далі створюється об'єкт Stats і додається до сцени. Це покаже нам, як швидко працює наш код.
<code data-language="javascript">//створення сцени scene=new THREE.Scene();
Остаток створюється Scene. ThreeJS використовує деревоподібну структуру під назвою граф сцени. Це корінь такого дерева. Все, що ми створюємо в сцені, буде дочірнім вузлом у дереві сцени. /window.innerHeight, 1, 10000); camera.position.set(0, 0, 5); scene.add(camera);
Далі йде камера з перспективою. Як правило, ви можете залишити ці значення, але при бажанні змінити положення камери.
<code data-language="javascript">//керування камерою
DragPanControls є об'єктом, який переміщатиме камеру навколо під час руху миші. Ви можете видалити його, якщо хочете, щоб у вас було інше управління.
<code data-language="javascript">//прозоро підтримує зміну розміру вікна THREEx.WindowResize.bind(renderer, camera); //при натисканні на "p" робить скріншот THREEx.Screenshot.bindKey(renderer); //при натисканні на «f» перетворюється на повноекранний режим, якщо підтримується if( THREEx.FullScreen.available()){ THREEx.FullScreen.bindKey(); document.getElementById('inlineDoc').innerHTML +="-f for fullscreen"; }
Зазвичай ми повинні обробляти зміну розмірів вікна вручну, але об'єкт Threex.WindowResize (підтримується шаблоном, не ThreeJS) буде обробляти його для нас . Він змінюватиме розмір сцени до розмірів вікна. Наступні рядки додають повноекранний режим через клавішу F та створення скріншота через клавішу P.
Добре, тепер ми пройшли через шаблон і можемо додати фігуру до сцени. Ми почнемо з тора, який має форму бублика. ThreeJS підтримує декілька стандартних фігур, включаючи тор.
<code data-language="javascript">//сюди додайте свої об'єкти //ви можете замінити цю частину на свій розсуд 1, 0.42); var material=New THREE.MeshNormalMaterial(); var mesh=new THREE.Mesh(geometry, material); scene.add( mesh);
Об'єкт у цій сцені називається mesh. Він складається з двох частин: geometry та material. Шаблон використовує геометрію тора та стандартний матеріал, який завжди відображає світло перпендикулярно до геометрії. Він відображає світло, але не містить вказівки кольору.
<code data-language="javascript">//цикл анімації function animate() { //цикл при запиті циклу анімації //— це має бути на початку функції //— докладніше дивись тут http: //my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating requestAnimationFrame( animate); //Візуалізація render(); //оновлюємо статистику stats.update(); }
Тепер перейдемо вниз до функції animate, яка викликає сама себе через requestAnimationFrame ( про яку ми дізналися в розділі про анімацію), звертається до render() і оновлює статистику.
<code data-language="javascript">//візуалізація сцени function render() { //оновлюємо керування камерою cameraControls.update(); //фактична візуалізація сцени renderer.render (scene, camera); }
Функція render викликається для кожного кадру анімації. Спочатку вона викликає оновлення керування камери, щоб дозволити рух камери у відповідь на дії миші та клавіатури. Потім викликає renderer.render, яка насправді малює сцену на екрані.
Це все. Ось як це виглядає:
Налаштування шаблону
Тепер давайте внесемо невеликі налаштування. Кожен об'єкт у сцені здатний масштабуватися, повертатися та змінювати положення. Повернемо тор через mesh.rotation.y=Math.PI/2. Зауважте, що повороти вказуються в радіанах, а не градусах. Math.PI/2 складає 90 градусів.
<code data-language="javascript">var geometry=new THREE.TorusGeometry( 1, 0.42); var material=New THREE.MeshNormalMaterial(); var mesh=new THREE.Mesh(geometry, material); mesh.rotation.y=Math.PI/2; //90 градусів
Тепер закоментуємо тор і замінимо його чимось складнішим. ThreeJS може використовувати збірні моделі так само добре, як і базові на кшталт тора. Чайник Юта є аналогом «Здрастуйте, світ» у тривимірній графіці, так що давайте почнемо з нього. Геометрія чайник кодується як файл JSON. Ми завантажуємо teapot.js з прикладів сховища і помістимо його в той же каталог, де index.html. Далі ми завантажуємо його через THREE.JSONLoader().load(). Після завершення завантаження ми додаємо його на сцену як нову модель, знову використовуючи стандартний матеріал.
<code data-language="javascript">//scene.add( mesh); new THREE.JSONLoader().load('teapot.js', function(geometry) { var material=new THREE.MeshNormalMaterial(); var mesh=new THREE.Mesh( geometry, material); scene.add( mesh); teapot=mesh;});
Тепер додамо анімацію і змусимо чайник обертатися на кожному кадрі. Ми просто встановимо змінну teapot і налаштуємо поворот на 0.01 у кожному кадрі.
<code data-language="javascript">//оновлення керування камери cameraControls. update(); teapot.rotation.y +=0.01;
Ефекти шейдера
Нарешті, ми додамо деякі ефекти пост-обробки. Вони називаються постобробкою, тому що відбуваються після основного етапу візуалізації. Ці частини ThreeJS API деякі експериментальні і не документовані добре, але я збираюся показати їх вам у будь-якому випадку, тому що вони дуже потужні. Пост-обробка вимагає підключення додаткових скриптів до нашої сторінки. Нам потрібні ShaderExtras.js, RenderPass.js, BloomPass.js, ShaderPass.js, EffectComposer.js, DotScreenPass.js та MaskPass.js.
<code data-language="html"><script src="vendor/three.js/ShaderExtras.js"></script> <script src="vendor/three.js/postprocessing/RenderPass.js"></script> <script src="vendor/three.js/postprocessing/BloomPass.js"></script> <script src="vendor/three.js/postprocessing/ShaderPass.js"></script> <script src="vendor/three.js/postprocessing/EffectComposer.js"></script> <script src="vendor/three.js/postprocessing/DotScreenPass.js"></script> <script src="vendor/three.js/postprocessing/MaskPass.js"></script>
Почнемо створення нової функції з ім'ям initPostProcessing(). Всередині неї створимо ефект композера.
<code data-language="javascript">function initPostProcessing() { composer=new THREE.EffectComposer(renderer);
Далі , ми додамо прохід візуалізації, що рендерує всю сцену в текстуру зображення. Ми повинні сказати, що це не потрібно показувати на екрані, а потім додати його до композер.
<code data-language="javascript">renderModel=new THREE.RenderPass(scene,camera); renderModel.renderToScreen=false; composer.addPass(renderModel);
Далі створимо екран з точками. Деякі значення є хорошими за замовчуванням, але ви можете налаштувати їх, щоб отримати різні ефекти. Цей прохід буде на екрані, так що ми встановимо renderToScreen як true і додамо його до композера.
<code data-language="javascript">var effectDotScreen=новий THREE.DotScreenPass( новий THREE.Vector2(0,0), 0.5, 0.8); effectDotScreen.renderToScreen=true; composer.addPass(effectDotScreen);
Тепер, нам необхідно оновити функцію render. Замість виклику renderer.render() ми будемо викликати renderer.clear() та composer. render().
<code data-language="javascript">//актуальна візуалізація сцени //renderer.render( scene, camera); //альтернативний запис renderer.clear(); composer.render();
Ми також повинні викликати initPostProcessing в останньому рядку функції init.
<code data-language="javascript">initPostProcessing();
Ось як це виглядає. Шалено, ага!
Просто з цікавості, якщо ми відкриємо ShaderExtras.js, побачимо фактичну математику шейдерів, яка створює шаблон точок і генерує фінальний колір кожного пікселя.
<code data-language="javascript">fragmentShader: [ "uniform vec2 center;", "uniform float angle;", "uniform float scale;", "uniform vec2 tSize;", " sampler2D tDiffuse;", "varying vec2 vUv;", "float pattern() {", "float s=sin( angle), c=cos( angle);", "vec2 tex=vUv * tSize — center; ", "vec2 point=vec2( c * tex.x — s * tex.y, s * tex.x + c * tex.y) * scale;", "return ( sin( point.x) * sin( point.y)) * 4.0;", "}", "void main() {", "vec4 color=texture2D( tDiffuse, vUv);" , "float average=( color.r + color.g + color.b) /3.0;", "gl_FragColor=vec4( vec3( average * 10.0 — 5.0 + pattern()) , color.a);", "}" ].join("\n")
Кілька деталей
Як і OpenGL, WebGL не підтримує текст безпосередньо. Натомість ви повинні намалювати текст за допомогою 2D Canvas, а потім додати його як текстуру на площину (див. WebGL Factor's explanation).
Існує бібліотека для швидкої побудови графічних інтерфейсів звана dat-gui. Сторінка проекту тут.
Є моделі завантажувачів для багатьох форматів. Ви, ймовірно, використовуєте завантажувач Collada або JSON. Деякі з них є лише геометрією, деякі включають текстури та анімацію, подібно до монструозних завантажувачів. Завантажувачі важливі, тому що найскладніша геометрія не створюється через код, натомість ви повинні використовувати геометрію створену кимось ще, ймовірно, із застосуванням 3D-інструментів моделювання на кшталт Blender або Maya.
Здебільшого, будь-які загальні поради щодо покращення продуктивності для OpenGL застосовні і до WebGL. Наприклад, ви завжди повинні кешувати геометрію та матеріали на графічному процесорі.
CreativeJS містить багато хороших прикладів 2D Canvas і WebGL.
У наступному розділі ви виконаєте практичну лабораторну, в якій створите нову програму з автомобілем, який їздить великою трав'янистою рівниною під зоряним небом.