Как стать автором
Обновить

Карта галактики на WebGL Three.js

Решил как-то исполнить свою давнюю идею — сделать карту галактики на three.js, т.е. на WebGL. Внимание: карту галактики, а не красивейшую визуализацию галактики.



Дисклаймер

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

Браузеры

Первая строчка такова:

if(Detector.webgl)
        renderer = new THREE.WebGLRenderer();
else
{
        alert("Увы, ваш браузер не поддерживает WebGL.")
        return;
}


Если речь идет о WebGL, сразу встает вопрос о браузерах. Я сознательно отказался от использования THREE.CanvasRenderer, ибо убедился эти два рендера во многом не совместимы, и то что работает с одним, с другим далеко не факт, что будет работать.

Тестировал только в виндовых версиях бразуеров, а именно в FF, Chrome, Opera. Opera Next, IE11. В FF и Хроме всё отлично работает, в Опере запускается, но работает плохо. В Opera Next (та инновационная, без закладок) всё работает отлично. IE11 какой-то странный, вроде поддержка WebGL есть, но когда я попытался запустить, то получил симпатичный такой лог:

THREE.WebGLRenderer 58THREE.WebGLRenderer: Float textures not supported.
THREE.WebGLRenderer: Standard derivatives not supported.
THREE.WebGLRenderer: Anisotropic texture filtering not supported.
THREE.WebGLRenderer: S3TC compressed textures not supported.

Причем любые примеры на Three.js именно так рисуются.

Я не хочу читать дальше, дай посмотреть!

Ну ладно, управление простейшее — drag&drop+mouse wheel, вот ссылка (надеюсь VPS выдержит): тык

Скучный Код

В общем плане код довольно обычен, вот, к примеру, инициализация:

renderer.setClearColor(0x000000);
renderer.setSize( window.innerWidth - 15, window.innerHeight - 15 );
renderer.sortObjects = false;
renderer.autoClear = false;
document.body.appendChild( renderer.domElement );

Создание сцен, используются как слои, для управления последовательностью отрисовки:

//Создаем сцены
scene = new THREE.Scene(); //Звезды и деления квадрантов
sceneNames = new THREE.Scene(); //Название звезд
sceneSectors = new THREE.Scene(); //Деления секторов

Самая обычная камера:
//Камера
camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 100000000 );
camera.position.set( 0, 0, 1000 );


Чуть менее Скучный Код

Как рисуется галактика? Используется логарифмическая спираль (wiki). Алгоритм взят отсюда: ссылка.
Код переписал на JS, каждая точка-звезда записывается в THREE.Geometry, и потом, вместе с материалом, создается система частиц. Если делать подобное без системы частиц, то высокого FPS'а точно не будет(звезд то 16 000, если создавать отдельные частицы, то… кхм.).

function initStars()
{
        particle_system_geometry = new THREE.Geometry();

	var x = 0, y = 0;
	var NUM_STARS = 16000;

	var A = 1.1;
	var B = 0.17;
	var WINDINGS = 3.7;
	var T_MAX = 2.0 * Math.PI * WINDINGS;		
	var DRIFT = 0.3

	for (var i = 0; i < 1800; i++) 
	{
		setStar(Math.sRandom(-1.7, 1.7), Math.sRandom(-1.7,1.7), GenShortName());
	}
	for (var i = 0; i < NUM_STARS; i++) 
	{
	  var t = T_MAX * Math.random();
	  var x = A * Math.exp(B * t) * Math.cos(t);
	  x = x + (DRIFT*x*Math.random()) - (DRIFT*x*Math.random());
	  var y = A * Math.exp(B * t) * Math.sin(t);
	  y = y + (DRIFT*y*Math.random()) - (DRIFT*y*Math.random())
	  if (Math.random() > 0.5)
	  {
 	     setStar(x, y, GenShortName());
	  }
          else
	  {
	     setStar(-x, -y, GenShortName());
	   }
	}

        ...

	var particle_system_material = new THREE.ParticleBasicMaterial({
          color: 0xeeeeee,
          size: 3
        });
	
        particleSystem = new THREE.ParticleSystem(
          particle_system_geometry,
            particle_system_material
        );
        scene.add(particleSystem);

}

Чуть-чуть кода генерации

Собственно функция генерации названий звездных систем. Написана была мною множество времени назад на C#, тоже перенес на JS.

function GenShortName()
{
    var abs = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "A", "S", "D", "F", "G",
                                             "H", "J", "K", "L", "Z", "X", "C", "V", "B",
                                             "N", "M"
                                         ];
    var result;

    var variant = Math.sRandom(0, 100);

    if (variant < 65)
    {
        //Элемент буквеный
        var el="";
        //Элемент численый
        var numEl="";
        //Максимум букв в буквеном элементе
        var maxCountAbs = 3;
        //Максимум цифр в цифреном элементе
        var maxCountNum = 2;
        //рандом количество
        var countAbs = Math.floor(Math.sRandom(1, maxCountAbs));

        for(var i = 0; i < countAbs; i++)
            el += abs[Math.floor(Math.sRandom(0, abs.length - 1)*Math.random())];

        //рандом количество
        var countNum = Math.floor(Math.sRandom(1, maxCountNum));

        for(var i = 0; i < countNum; i++)
            numEl += Math.floor(Math.sRandom(0, 9)).toString();

        result = el+"-"+numEl;
    }

    if (variant >= 65)
    {
        //Элемент буквеный
        var el="";
        //Элемент буквеный 2
        var el2="";
        //Максимум букв в буквеном элементе
        var maxCountEl = 3;
        //Максимум букв в буквеном элементе 2
        var maxCountEl2 = 3;
        //рандом количество
        var countAbs = Math.floor(Math.sRandom(1, maxCountEl));

        for(var i = 0; i < countAbs; i++)
            el += abs[Math.floor(Math.sRandom(0, abs.Count - 1)*Math.random())];

        //рандом количество
        var countNum = Math.floor(Math.sRandom(1, maxCountEl2));

        for(var i = 0; i < countNum; i++)
            el2 += abs[Math.floor(Math.sRandom(0, abs.Count - 1)*Math.random())];

        result = el+"-"+el2;
    }


    return result;
}


Заключение

Ну, вот, в основном, самое интересное. Код выложил на гитхаб, если есть какие-то советы по улучшению, буду рад услышать.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.