Световые ореолы
Узнайте больше, создав форк полного проекта.
Этот простой эффект отлично подходит для добавления атмосферы в вашу сцену. Добавьте свечение к источнику света или эмиссионной текстуре, чтобы создать эффект пыльной или туманной атмосферы или смоделировать эффект просмотра яркого света.
Он работает следующим образом: мы создаем сущность с прикрепленным примитивом плоскости, на котором находится материал светящегося ореола. К сущности мы прикрепляем скрипт, который заставляет плоскость всегда смотреть на камеру (billboarding). Для дополнительного удовольствия мы затушевываем ореол, если он направлен в сторону от камеры, чтобы имитировать направленный свет.
Assets
Текстура
Сначала вам понадобится текстура ореола. В этом примере мы просто использовали очень простую размытую кляксу, созданную в художественной программе, такой как Photoshop.
Эта текстура будет основой свечения.
Материал
Материал для светового ореола использует текстуру кляксы в слоте эмиссии. Используйте свойство tint для установки цвета вашего ореола. Мы также включили смешивание в слоте прозрачности, и он также использует текстуру кляксы с Color Channel установленным на R.
Blend Type установлен на Additive Alpha. Часть Additive означает, что цвет материала
добавляется к цвету фона под ним. Это означает, что ореол светится на фоне. Часть Alpha означает, что она использует значение
opacity
для установки прозрачности материала.
Сущности
Настройка сущностей для свечения тоже проста. У нас есть родительская сущность для скрипта ореола и дочерняя сущность, к которой прикреплен
примитив плоскости. Мы делаем это для упрощения кода, чтобы мы могли использовать entity.lookAt
для установки ориентации свечения.
Примитив плоскости обращен вверх, поэтому мы создаем дочернюю сущность и применяем к ней вращение, чтобы плоскость была правильно размещена
перед камерой.
Код
Код для этого проекта имеет две особенно интересные функции.
Во-первых, мы обновляем сущность ореола, чтобы она смотрела на камеру каждый кадр
// Установить свечение так, чтобы оно всегда было направлено на камеру
this.entity.lookAt(this.camera.getPosition());
Во-вторых, если гало помечено как unidirectional
(с атрибутом скрипта, который мы предоставили), то мы изменяем прозрачность так,
чтобы гало было невидимым, когда оно направлено в противоположную сторону от камеры. На самом деле мы постепенно изменяем прозрачность так,
чтобы она становилась более прозрачной, чем больше она указывает в противоположную сторону от камеры.
// Если включено, однонаправленное означает, что свечение исчезает, когда оно отводится от камеры
if (this.unidirectional) {
// Получить скалярное произведение направления родителя и направления камеры
var dot = -1 * tmp.dot(this.camera.forward);
// Если скалярное произведение меньше 0, свечение направлено от камеры
if (dot < 0) {
dot = 0;
}
// Переопределить значение прозрачности на экземпляре сетки плоскости, чтобы уменьшить его до нуля, когда свечение отводится от камеры
meshes[0].setParameter("material_opacity", dot);
} else {
// Необходимо установить значение по умолчанию из-за этой проблемы на данный момент: https://github.com/playcanvas/engine/issues/453
meshes[0].setParameter("material_opacity", 1);
}
Мы используем метод setParameter
на pc.MeshInstance для установки значения, которое будет
использоваться фрагментным шейдером. В настоящее время это является недокументированной функцией в движке (вы не найдете ее по ссылке на
документацию для разработчиков). Причина этого заключается в том, что она зависит от точного знания имени униформной переменной в шейдере. Эти
значения могут измениться, и этот API может измениться в будущем. Но это довольно полезно, потому что позволяет переопределить одно значение в
шейдере только для этого экземпляра сетки. В данном случае это важно, потому что все свечения используют один и тот же материал, но нам
потребуется разное значение прозрачности для каждого экземпляра свечения.
Вот полный список:
var Halo = pc.createScript('halo');
Halo.attributes.add('camera', {type: 'entity'});
Halo.attributes.add('unidirectional', {type: 'boolean', default: false});
Halo.tmp = new pc.Vec3();
// инициализация кода, вызываемая один раз для каждой сущности
Halo.prototype.initialize = function() {
// Получить сущность с моделью плоскости на ней
this.plane = this.entity.children[0];
// Получить родительскую сущность, которая используется для указания направления
this.parent = this.entity.parent;
};
// код обновления, вызываемый каждый кадр
Halo.prototype.update = function(dt) {
var tmp = Halo.tmp;
// Сохранить вектор, в котором родитель смотрит (обратите внимание, что вперед - это отрицательное z)
tmp.copy(this.parent.forward).scale(-1);
var meshes = this.plane.render.meshInstances;
if (this.camera) {
// Установить свечение так, чтобы оно всегда было направлено на камеру
this.entity.lookAt(this.camera.getPosition());
// Если включено, однонаправленное означает, что свечение исчезает, когда оно отклоняется от камеры
if (this.unidirectional) {
// Получить скалярное произведение направления родителя и направления камеры
var dot = -1 * tmp.dot(this.camera.forward);
// Если скалярное произведение меньше 0, свечение направлено от камеры
if (dot < 0) {
dot = 0;
}
// Переопределить значение прозрачности на экземпляре сетки плоскости, чтобы оно исчезало до нуля, когда свечение отклоняется от камеры
meshes[0].setParameter("material_opacity", dot);
} else {
// Необходимо установить значение по умолчанию из-за этой проблемы на данный момент: https://github.com/playcanvas/engine/issues/453
meshes[0].setParameter("material_opacity", 1);
}
}
};
Вот и все. Простой, но красивый эффект для добавления в вашу сцену. Посмотрите проект для получения дополнительной информации.