Dart + WebGL так ли вкусны печеньки на тёмной стороне

от автора


История развития веб-приложений очень сильно напоминает жизнь Энакена Скайуокера. Лет 15 назад они были слабыми и неуклюжими. Но вот прошло время, тестостерон бьёт в голову и наш “Энакен” падает в раскалённую лаву требований к качеству и функциональности браузерных программ. Своей единственной уцелевшей джаваскриптой он подгребает под себя землю. Здесь внимательный читатель заметит, что так мы далеко не уползём. Неожиданный, а на самом деле вполне закономерный сюжетный поворот и на сцене появляется два спасителя Dart и TypeScript. Давайте попробуем встать на сторону одного из трёх персонажей.

Почему Dart, просто потому, что он типизированный и у него си-подобный синтаксис. TypeScript на первый взгляд ничем не хуже, но Microsoft, который разрабатывает этот язык, очень любит играть в одни ворота, что не добавляет ему плюсов. К тому же Google разрабатывает VirtualMachine для Dart, что в перспективе может дать значительное преимущество в производительности этого языка.

Почему WebGL, потому, что эта технология, как и Dart, находится на передовой, и 3D в браузере, на мой взгляд, это очень интересная тема.

Ожидать от браузерной технологии качества настольных игр не имеет смысла. WebGL это OpenGL 2.0, а значит у нас в руках только вершинные и фрагментные шейдеры, а это значит, что качество картинки будет отставать от настольных игр примерно на 5 лет. Для мастеров своего дела это не преграда, танки онлайн это доказали, а в руках создателей первых танков даже этого не было.

Для начала нам потребуется IDE Dart editor. Его можно скачать с официального сайта www.dartlang.org/
В основе этой IDE лежит, многим привычный, Eclips, поэтому, желательно, скачивать версию той битности, какой у вас установлена Java. Иначе не удастся запустить.

Создадим новый проект: File->New application.

import 'dart:html';  void main() { } 

Это минимальная комплектация приложения на Dart. Остальное можно смело удалять.
Чтобы получить в свои руки элемент со страницы с определённым id, надо вызвать функцию query вот так:
var someElement = query(“#someid”);
Взглянем на минимальный html файл, который мы будем использовать для экспериментов:

<!DOCTYPE html> <html> <body> <!-- Выделяем место для сообщений из кода-->     <p id="status"></p> <!-- Создаём холст для рисования-->    <canvas id="canvas3d" width="800" height="600"></canvas> <!-- Подключаем рабочий код для страницы -->    <script src="Simple.dart" type="application/dart"></script>    <script src="http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js"></script>  </body> </html> 

Вот так будет выглядеть код на Dart выводящий короткое сообщение на страницу:

import 'dart:html';  void main() {     var status = query('#status');     status.innerHtml = '<p>Добро пожаловать в мир Dart.</p>'; } 

Проверить работоспособность можно нажав кнопку запуска:

А скомпилировать для публикации в веб — нажав ПКМ на dart файл в окне с деревом файлов проекта и нажав “Run as JavaScript”:

Для вывода 3D графики нам надо получить 3D контекст от холста на котором мы будем рисовать.

//Получаем холст canvas = document.query('#canvas3d'); //Берём webGl контекст для вывода графики gl = (canvas.getContext("webgl")!=null)?canvas.getContext("webgl") :   canvas.getContext("experimental-webgl"); 

Если после этого запроса gl равен null, значит браузер не поддерживает webGl.

if (gl == null)  {     status.innerHtml = '<p>Простите, ваш браузер не поддерживает WebGl</p>'; } 

После получения контекста, надо инициализировать его, передав в качестве параметров сдвиг по горизонтали, сдвиг по вертикали, ширину и высоту рамки отображения:

gl.viewport(0, 0, canvas.width, canvas.height); 

Чтобы отобразить хоть что-то на холсте, надо загрузить два шейдера, один вершинный и один фрагментный(он же пиксельный).

//Пиксельный шейдер String vsSource = """     attribute vec3 aPosition;     void main()      {         gl_Position = vec4(aPosition, 1);     }""";     // Фрагментный шейдер String fsSource = """     precision mediump float;     uniform vec4 uColor;     void main() {         gl_FragColor = uColor;     }"""; //Шейдеры написаны, теперь их надо скомпилировать и загрузить в контекст. //Компилируем WebGLShader vs = gl.createShader(WebGLRenderingContext.VERTEX_SHADER); gl.shaderSource(vs, vsSource); gl.compileShader(vs); WebGLShader fs = gl.createShader(WebGLRenderingContext.FRAGMENT_SHADER); gl.shaderSource(fs, fsSource); gl.compileShader(fs);     // Загружаем шейдеры в GPU WebGLProgram p = gl.createProgram(); gl.attachShader(p, vs); gl.attachShader(p, fs); gl.linkProgram(p); gl.useProgram(p); //Проверяем всё ли удачно скомпилировалось. if (!gl.getShaderParameter(vs, WebGLRenderingContext.COMPILE_STATUS)) {      print(gl.getShaderInfoLog(vs)); }     if (!gl.getShaderParameter(fs, WebGLRenderingContext.COMPILE_STATUS)) {      print(gl.getShaderInfoLog(fs)); }     if (!gl.getProgramParameter(p, WebGLRenderingContext.LINK_STATUS)) {      print(gl.getProgramInfoLog(p)); } 

Команда print выводит информацию в дебажную консоль используемой IDE. Если что-то пошло не так, то о причинах можно будет прочитать.
Теперь надо загрузить вершинный буфер в GPU:

//Задаём координаты в трехмерном пространстве Float32Array vertices = new Float32Array.fromList([                                                       -0.4, 0.4, 1,                                                       0.4,  0.4, 1,                                                       -0.4, -0.4,1                                                       ]); //Создаём буфер и загружаем в него координаты gl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, gl.createBuffer()); gl.bufferData(WebGLRenderingContext.ARRAY_BUFFER,                       vertices, WebGLRenderingContext.STATIC_DRAW); //Указываем сколько вершин нарисовано int numItems = 3; // Устанавливаем позицию, которая будет передана в вершинный шейдер int aPosition = gl.getAttribLocation(program, "aPosition"); gl.enableVertexAttribArray(aPosition); gl.vertexAttribPointer(aPosition, 3, WebGLRenderingContext.FLOAT, false, 0, 0);  //Очищаем холст заливая его новым цветом(RedGreenBlueAlpha) gl.clearColor(0.9, 0.9, 0.9, 1); gl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT);  // Устанавливаем цвет, который будет передан фрагментному шейдеру WebGLUniformLocation uColor = gl.getUniformLocation(program, "uColor"); // Как и для очистки холста он задаётся в формате RGBA gl.uniform4fv(uColor, new Float32Array.fromList([0.5, 0.9, 0.9, 1.0]));     

Единственное, что нам осталось сделать, это дать команду отрисовки:

gl.drawArrays(WebGLRenderingContext.TRIANGLES, 0, numItems); 

Теперь можно запустить проект и посмотреть на результат:

Скачать целый исходный файл dart вы можете здесь: dl.dropbox.com/u/41059365/Simple.dart
Целью данной статьи было показать что Dart может то же, что и JS, но удобнее. Статическая типизация и автодополнение сильно сокращают время разработки, позволяя не держать в голове ненужную информацию. Об этих и других плюшках хорошо рассказывают в этом видео:

Наш ситх всё еще молод, но он быстро меняется и взрослеет. WebGl крепнет вместе с ним, пусть и несколько медленее. Сравнивая Dart и JS хочется выразить глубочайшее почтение к тем, кто способен написать действительно сложные проекты на втором из них. Количество сторонних библиотек для Dart в несоизмеримо меньшем числе чем для JS, но язык и инструменты прилагающиеся к нему настолько хороши, что за новыми проектами не заржавеет. А пошаговая отладка кода, это сокровище для начинающих программистов. Печеньки на стороне Dart и вскоре он станет властелином всего интернета.

ссылка на оригинал статьи http://habrahabr.ru/post/161785/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *