История развития веб-приложений очень сильно напоминает жизнь Энакена Скайуокера. Лет 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 и вскоре он станет властелином всего интернета.
Автор: Vilyx