Эффект затенения

«Честные» динамические тени в аркадных 3D-играх не всегда бывают уместны. Как правило, разработчики ограничиваются статическими предрассчитанными тенями от неподвижных объектов и простым темным кружочком на земле под персонажем – дешево и сердито =) 
Я решил дополнить этот нехитрый метод одной простой, но важной деталью: изменение яркости персонажа в зависимости от его местоположения – в тени или на свету. 

Цвет для яркости будет считываться из карты освещения, в которой «запечены» все статические тени на карте.

Допустим, у вас есть некий блок кода, в котором вы находите точку на поверхности полигона под ногами персонажа:

// Проверяем факт пересечения
IntrStatus istatus = character.downRay.intersectTriangle(tri);
if (istatus.hit)
{
// Извлекаем точку пересечения
Vector3f ipt = istatus.intersectionPoint;

if (ipt.y > currentFloorHeight)
{
currentFloorHeight = ipt.y;

// Берем материал полигона
Material mat = materialByIndex[tri.matIndex];

// Если нет карты освещения (текстура 1), то ничего не делаем
if (mat.textures[1])
{
// Берем изображение карты освещения
Image lightmap = imageByIndex[mat.textures[1].imgIndex];

// Находим текстурные координаты точки пересечения
Vector2f tc = triObjSpaceToTexSpace(tri.vertices, tri.texCoords2, ipt);

// Конвертируем текстурные координаты в дискретные
uint imgX = cast(uint)(tc.x * lightmap.width - 0.5f);
uint imgY = cast(uint)(tc.y * lightmap.height - 0.5f);

// Считываем цвет пикселя c карты освещения
Color lumel = lightmap[imgX, imgY];

// Применяем полученный цвет к материалу персонажа
chMaterial.ambientColor = lumel;
chMaterial.diffuseColor = lumel;
chMaterial.specularColor = lumel;
}
}
}

Осталось определить функцию triObjSpaceToTexSpace. Она будет использовать барицентрические координаты:

Vector2f triObjSpaceToTexSpace(
Vector3f[3] vertices,
Vector2f[3] texCoords,
Vector3f point)
{
Vector3f v0 = vertices[2] - vertices[0];
Vector3f v1 = vertices[1] - vertices[0];
Vector3f v2 = point - vertices[0];

float dot00 = dot(v0, v0);
float dot01 = dot(v0, v1);
float dot02 = dot(v0, v2);
float dot11 = dot(v1, v1);
float dot12 = dot(v1, v2);

float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

Vector2f t2 = texCoords[1] - texCoords[0];
Vector2f t1 = texCoords[2] - texCoords[0];

return texCoords[0] + t1 * u + t2 * v;
}

Atrium собирается последней версией LDC

Совсем недавно вышла бета-версия готовящегося к релизу LDC 0.10.0 – компилятора D, использующего LLVM для генерации машинного кода.

Решив проверить LDC на практике, я только что собрал им Atrium. Результат очень обрадовал, при компиляции в релизном режиме и с уровнем оптимизаций -O3, LDC выдал весьма шустрый код: ~100 FPS против ~80 у DMD 2.060! При этом я всего один раз столкнулся с необходимостью поправить исходники – при использовании ассоциативного массива указателей.

Как вывод: LDC вполне созрел для использовании в качестве основного компилятора D под Linux – во всяком случае, для работы с OpenGL и графикой реального времени. К сожалению, я не в курсе, как у него обстоят дела c поддержкой Windows – надеюсь, со временем этот вопрос устаканится. 

Журнал “FPS” №21

Вышел 21 номер электронного PDF-журнала “FPS“, посвященного разработке игр, программированию, компьютерной графике и звуку. В этом номере можно отметить следующие материалы:

> Blender: обзор дополнений (выпуск 3)
> Tears of Steel: роботы тоже плачут…
> Эффект “старинного фото” в GIMP
> Журнал GIMP Magazine
> Язык D: новости
> Семь мифов о D
> Сборщик мусора – враг или друг?
> Юникод в OpenGL. Раз и навсегда
> Почему я выбираю Linux?
> Патентные войны

Номер доступен для онлайн-чтения и загрузки на сервисе Issuu.com и Документах Google. Последние новости по проекту вы можете узнать в публичной странице журнала в социальной сети Google+.

Передискретизация в dlib.image

Передискретизация (ресэмплинг) изображения – это всего-навсего изменение его разрешения в пикселях. Такую функцию должна включать любая уважающая себя библиотека обработки изображений, и с сегодняшнего дня dlib – не исключение. В пакете dlib.image.resampling доступны четыре алгоритма ресэмплинга, которые часто встречаются в графических редакторах: линейная интерполяция (Linear или Nearest Neighbor), билинейная (Bilinear), бикубическая (Bicubic) и фильтр Ланцоша (Lanczos). Ниже представлены результаты их работы на примере увеличения разрешения картинки с 64х64 до 256х256.

Оригинал




Linear
Самый простой и, следовательно, самый быстрый способ изменить размеры изображения. Известен также как интерполяция методом ближайшего соседа (Nearest Neighbor). Для промежуточных пикселей выбирается ближайшее известное значение.

Bilinear
Для получения промежуточного значения производится линейная интерполяция между четырьмя ближайшими пикселями. Это позволяет несколько сгладить переходы. Билинейный фильтр – неплохой компромисс между скоростью и качеством результата.

Bicubic
Промежуточные значения вычисляются путем свертки ядра 4×4 специальной бикубической функцией. Достаточно медленный алгоритм, но зато дает наименее пикселизированный результат.

Lanczos
У бикубической интерполяции есть недостаток – изображение получается несколько размытым. Чтобы сохранить четкость, можно воспользоваться фильтром Ланцоша. Он немного увеличивает пикселизацию, но создает контрастные области вдоль контуров для повышения четкости. В моей реализации я использовал ядро 7х7. Очень медленно, зато качественно.

Существуют и другие алгоритмы – постепенно они тоже будут добавлены в библиотеку.
Изменения доступны в dlib ревизии r5 и выше.

Начать – наполовину сделать

В этом блоге я буду публиковать свой прогресс в области разработки 3D-игр на языке программирования D с использованием OpenGL. Хочу предупредить – ничего определенного я не обещаю, геймдев для меня – всего лишь хобби. Я не из тех, кто ставит перед собой заведомо недостижимые цели – мне нравится сам процесс =)