Визуализация ближайших звезд «100000 Stars»

Майкл Чанг, из команды Google на сайте HTML5rocks написал интересную статью, о окончании эксперимента Chrome, визуализация ближайших звезд «100000 Stars». Проект был разработан с THREE.js и CSS3D. Он описал часть проекта, управление шейдеров, и как использовать текст CSS в сочетании с WebGL.
Демо
Цель написания статьи, показать возможности современных веб-технологий русскоязычным пользователям интернета, в данном случае используется THREE.js для визуализации близлежащих звезд в Млечном Пути. Технологию WebGL, на момент написания этой статьи, корректно поддерживает только Google Chrome!

Пространство

После того, как был завершен Глобус , проводились эксперименты с THREE.js. Оказалось что есть возможность менять масштаб сцены, и увеличивать количество эффектов.

Когда глубина резкости была действительно экстремальной, удаленные объекты стали действительно размыты подобно тому, как со сдвигом и наклоном работает фотография создавая иллюзию, глядя на микроскопические сцены. Наклонив, эффект делал вид, как если бы вы смотрели в глубокий космос.

Для размещения звезд в сцене, использовалась база астрономии astronexus.com .

Построение данных звезд
Первый шаг для размещения каждой звезды в каталоге, как одна частица.
Имена звезд
Некоторые звезды в каталоге имеют собственные имена, обозначенные здесь.

Размещение звёзд из данных астрономии в 3D-пространстве не заняло много времени, хотя в наборе ровно 119617 звезд, для современных видеокарт GPU это не составило труда. Для индивидуально определённых звезд, использовано наложение CSS, используя такой же метод, как и для глобуса.

Создание галактики

На мой взгляд, Майклу удалось с генерировать модель галактики, с потрясающим видом Млечного Пути.

Ранний прототип галактики
Прототип частиц системы Млечного Пути

Для формирования Млечного Пути, добавлено 100000 частиц и размещенных по спирали.
Сформировать Млечный Путь пытались многие, используя частицы как плоское изображение галактики для частиц, и придать Млечному Путь больше реальный внешний вид.


Изображение спиральной галактики NGC 1232Б, примерно 70 миллионов световых лет от земли.
Выяснить масштабы галактики
Каждая единица GL является световой год. В этом случае сфера шириной 110 тысяч световых лет
  • Один блок GL — это пиксель в 3D, как один световой год

Сначала было решение поворачивать саму сцену галактики, а не перемещать камеру. Но нужно приблизить и посмотреть что находится в вертушке, так что бы мышкой перетаскивать влево и вправо, поворачивать объект, а масштабирование это всего лишь вопрос изменения camera.position.z.

Различные способы отобразить галактику
Сравните с Прототипом частиц и частицы сопровождаются плоским изображением.

На этом этапе в состоянии представить себе относительные размеры Солнечной системы путем сопоставления из радиуса для визуализации выбрано Облако Оорта как модель солнечной системы, можно также визуализировать упрощенную орбиту Земли, и фактический радиус солнца в сравнении.

Солнечная система
Орбита солнца, и планеты в сфере, представляют пояса Койпера.

Солнце трудно было сделать. Поверхность Солнца является горячим гелием плазмы для этого необходим импульс что бы он менялся с течением времени. Было смоделировано с помощью растровой текстуры инфракрасное изображение поверхности Солнца. Поверхность шейдеров делает цвет, основан на градации серого этой текстуры и выполняет вид, в отдельном рампы цвета. Когда этот вид деятельности смещается с течением времени он создает эти лавообразные искажения.

Подобная техника была использована для короны Солнца, кроме того, что это будет плоский спрайт карта, которая всегда обращена к камере с помощью THREE.Gyroscope (); .

Ранние версии Солнца
Ранние версии Солнца

Солнечные вспышки были созданы с помощью шейдеров, спиннинг только по краям поверхности Солнца. Шейдеров с функцией шум.
Здесь появились некоторые вопросы в связи с точностью GL. Все переменные для точности были предварительно определены в THREE.js, когда моделировались другие звездные системы для увеличения точности было очень много работы.

Модель звезд
Код для Солнца и других звезд.

Были несколько хаков я использовал для смягчения Material.polygonoffset . Это было использовано, чтобы заставить плоскость быть на верхней часть поверхности Солнца и реализовались световые лучи летящие от сферы.

Другая проблема связана с точностью в том, что модели звезд начинают дрожать когда сцена увеличивается в масштабе. Чтобы исправить это, я должен был «обнулить» сцену вращения и отдельно повернуть модель карты звезды и окружающей среды, чтобы создать иллюзию, что вы видите звезды.

Реализовать блик

С большой силой приходит большая ответственность
С большой силой приходит большая ответственность.

Визуализация космоса с использованием LensFlare. THREE.LensFlare именно для этой цели, все, что нужно сделать добавить в сцену.



//	This function retuns a lesnflare THREE object to be .add()ed to the scene graph
function addLensFlare(x,y,z, size, overrideImage){
  var flareColor = new THREE.Color( 0xffffff );

  lensFlare = new THREE.LensFlare( overrideImage, 700, 0.0, THREE.AdditiveBlending, flareColor );

  //	we're going to be using multiple sub-lens-flare artifacts, each with a different size
  lensFlare.add( textureFlare1, 4096, 0.0, THREE.AdditiveBlending );
  lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
  lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
  lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );

  //	and run each through a function below
  lensFlare.customUpdateCallback = lensFlareUpdateCallback;

  lensFlare.position = new THREE.Vector3(x,y,z);
  lensFlare.size = size ? size : 16000 ;
  return lensFlare;
}

//	this function will operate over each lensflare artifact, moving them around the screen
function lensFlareUpdateCallback( object ) {
  var f, fl = this.lensFlares.length;
  var flare;
  var vecX = -this.positionScreen.x * 2;
  var vecY = -this.positionScreen.y * 2;
  var size = object.size ? object.size : 16000;

  var camDistance = camera.position.length();

  for( f = 0; f < fl; f ++ ) {
    flare = this.lensFlares[ f ];

    flare.x = this.positionScreen.x + vecX * flare.distance;
    flare.y = this.positionScreen.y + vecY * flare.distance;

    flare.scale = size / camDistance;
    flare.rotation = 0;
  }
}


Текстура

Текстура прокрутки
Плоскость, для помощи ориентации в пространстве.

THREE.CylinderGeometry() применялась для солнца. Для создания луча света, меняем текстуру смещения во времени следующим образом:



mesh.material.map.needsUpdate = true;
mesh.material.map.onUpdate = function(){
  this.offset.y -= 0.001;
  this.needsUpdate = true;
}


Цвет звёзд

Каждая звезда имеет различные цвета, основанные на color-index. Короче, красные синие фиолетовые звезды горячее, белые и оранжевые промежуточные.

Покрасить каждую звезду своим цветом на основе этих данных. Способ сделать это было с атрибутами шейдер материала, применяемого к частицам.



var shaderMaterial = new THREE.ShaderMaterial( {
  uniforms: 	datastarUniforms,
  attributes:     datastarAttributes,
  /* ... etc */
});




var datastarAttributes = {
  size: 		{ type: 'f', value: [] },
  colorIndex: 	{ type: 'f', value: [] },
};


Заполнение массива ColorIndex что бы каждая частица имела свой уникальный цвет.

Цвет рампы
Цветовая шкала для поиска нужного цвета от показаний цвета звезды.

Получить доступ к растровым данным цвета с помощью JavaScript. Сначала загрузится изображение на DOM, втянуть его в графический элемент, для доступа к графическому растровому изображению.



//	make a blank canvas, sized to the image, in this case gradientImage is a dom image element
gradientCanvas = document.createElement('canvas');
gradientCanvas.width = gradientImage.width;
gradientCanvas.height = gradientImage.height;

//	draw the image
gradientCanvas.getContext('2d').drawImage( gradientImage, 0, 0, gradientImage.width, gradientImage.height );

//	a function to grab the pixel color based on a normalized percentage value
gradientCanvas.getColor = function( percentage ){
  return this.getContext('2d').getImageData(percentage * gradientImage.width,0, 1, 1).data;
}


Этот метод используется и для окраски отдельных звезд в модели звёзд.

Окраска отдельных звезд в модели звёзд
Этот же прием используется, чтобы сделать цвет поиска для спектрального класса звезды.

Количество шейдеров

В проекте используется много шейдеров для выполнения всех визуальных эффектов, поэтому Майкл Чанг написан загрузчик шейдеров.



//	list of shaders we'll load
var shaderList = ['shaders/starsurface', 'shaders/starhalo', 'shaders/starflare', 'shaders/galacticstars', /*...etc...*/];

//	a small util to pre-fetch all shaders and put them in a data structure (replacing the list above)
function loadShaders( list, callback ){
  var shaders = {};

  var expectedFiles = list.length * 2;
  var loadedFiles = 0;

  function makeCallback( name, type ){
    return function(data){
      if( shaders[name] === undefined ){
        shaders[name] = {};
      }

      shaders[name][type] = data;

      //  check if done
      loadedFiles++;
      if( loadedFiles == expectedFiles ){
        callback( shaders );
      }

    };
  }

  for( var i=0; i<list.length; i++ ){
    var vertexShaderFile = list[i] + '.vsh';
    var fragmentShaderFile = list[i] + '.fsh';

    //	find the filename, use it as the identifier
    var splitted = list[i].split('/');
    var shaderName = splitted[splitted.length-1];
    $(document).load( vertexShaderFile, makeCallback(shaderName, 'vertex') );
    $(document).load( fragmentShaderFile,  makeCallback(shaderName, 'fragment') );
  }
}


LoadShaders() принимает список имен файлов шейдеров, пытается загрузить свои данные, а потом просто заменяет список с объектами. В итоге в THREE.js форму можно передать шейдеры к нему вот так:



var galacticShaderMaterial = new THREE.ShaderMaterial( {
  vertexShader:   shaderList.galacticstars.vertex,
  fragmentShader: shaderList.galacticstars.fragment,
  /*...*/
});


CSS и надписи в верхней части THREE.JS

В проекте, Глобус, отображаются текстовые метки в верхней части сцены THREE.js. Для отображения меток в нужное положение экрана использовался THREE.Projector() , и разметка таблицей CSS в нужное место.

CSS3D разместит текст на верхнюю часть WebGL для этого посмотрите исходный код. Обновите функцию матрицы THREE.js:



/* Fixes the difference between WebGL coordinates to CSS coordinates */
function toCSSMatrix(threeMat4, b) {
  var a = threeMat4, f;
  if (b) {
    f = [
      a.elements[0], -a.elements[1], a.elements[2], a.elements[3],
      a.elements[4], -a.elements[5], a.elements[6], a.elements[7],
      a.elements[8], -a.elements[9], a.elements[10], a.elements[11],
      a.elements[12], -a.elements[13], a.elements[14], a.elements[15]
    ];
  } else {
    f = [
      a.elements[0], a.elements[1], a.elements[2], a.elements[3],
      a.elements[4], a.elements[5], a.elements[6], a.elements[7],
      a.elements[8], a.elements[9], a.elements[10], a.elements[11],
      a.elements[12], a.elements[13], a.elements[14], a.elements[15]
    ];
  }
  for (var e in f) {
    f[e] = epsilon(f[e]);
  }
  return "matrix3d(" + f.join(",") + ")";
}


Теперь текст больше не маячить перед камерой. Для этого использовали THREE.Gyroscope () , которая заставляет Object3D терять свою ориентацию унаследовав от сцены. Эта техника называется «billboarding», и гироскопом идеально подходит для этого.

CSS и надписи в верхней части
Метки всегда выпадают с камеры, подключив его к THREE.Gyroscope().

Звуки

Я использовал тэг аудио для воспроизведения музыки, однако даже в Chrome ненадежно — иногда он просто не в петлю. В конце этой двойной аудио хак тег используется для проверки конца воспроизведения и велосипедные к другой тег для игры. Что было разочарование в том, что это * еще * не идеальный цикл все время, увы, я чувствую, что это было лучшее, что я мог сделать.



var musicA = document.getElementById('bgmusicA');
var musicB = document.getElementById('bgmusicB');
musicA.addEventListener('ended', function(){
  this.currentTime = 0;
  this.pause();
  var playB = function(){
    musicB.play();
  }
  //	make it wait 15 seconds before playing again
  setTimeout( playB, 15000 );
}, false);

musicB.addEventListener('ended', function(){
  this.currentTime = 0;
  this.pause();
  var playA = function(){
    musicA.play();
  }
  //	otherwise the music will drive you insane
  setTimeout( playA, 15000 );
}, false);

//	okay so there's a bit of code redundancy, I admit it
musicA.play();


В заключение

Возможно, скоро в Googlemap мы увидим не только 3D карту земли но и карту нашей галактики и проложить короткий путь от Марса до Венеры :-D. Современные веб-технологии позволяют это сделать, не только для космических игр, но и для браузера.

2 комментария на тему “Визуализация ближайших звезд «100000 Stars»”

  1. комментарии

    Интернет развивается с ним и веб-технологии становятся потрясающие.

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

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

Можно использовать следующие HTML -теги и атрибуты: <a href= http://pixelcom.crimea.ua/"" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>