Декали в играх


Notice: Функция get_currentuserinfo с версии 4.5.0 считается устаревшей! Используйте wp_get_current_user(). in /hlds/web/u138079p19/code4life.ru/htdocs/wp-includes/functions.php on line 3840

Декали (decal с англ.: бирка, ярлык, клеймо) это особый вид геометрии, позволяющий детализировать игровое окружение. Чаще всего декали используются для отрисовки следов от пуль, взрывов; пятен крови; трещин и потертостей на стенах. Они могут быть как динамическими — созданными в процессе работы движка, так и статическими — размещенными дизайнером уровня. Мы рассмотрим способ создания декалей путем оборачивания геометрии уровня.

Этот способ подразумевает копирование участка геометрии с заменой текстуры и состоит из нескольких этапов:

  1. Получение облака треугольников в некотором радиусе от точки размещения (попадания)
  2. Для каждого треугольника построить базис в его текстурных координатах
  3. Найти область пересечения треугольника с прямоугольником декали в полученном с предыдущем пункте базисе и рассчитать текстурные координаты
Реализация
0. Подготовка

На этом этапе необходимо узнать размеры текстуры декали, которая будет использоваться и рассчитать размеры декали.

// Размеры декали
float fDecalWidth = fTexWidth * fScale;
float fDecalHeight = fTexHeight * fScale;

// Ограничители в пространстве декали
float2 sBound(fDecalWidth * 0.5f, fDecalWidth * 0.5f);
float2 tBound(fDecalHeight * 0.5f, fDecalHeight * 0.5f);

// "Радиус" декали
float fRadius = sqrtf((sBound.x - sBound.y) * (sBound.x - sBound.y) + (tBound.x - tBound.y) * (tBound.x - tBound.y));

Где fTexWidth, fTexHeight — размеры текстуры декали в пикселях, fScale — масштаб декали. У нас он равен 0.0016 для базового размера.

1. Получение облака треугольников

Теперь необходимо получить облако треугольников в радиусе fRadius от центра декали (который мы задаем сами). При этом лучше «захватить» чуть больше, чем на самом деле нужно, лишние треугольники будут отброшены на следующих этапах. В нашем движке SkyXEngine в данный момент для этих целей используется запрос к подсистеме физики. Подробно на этом пункте останавливаться не буду.

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

2. Построение базиса в текстурном пространстве треугольника

Координаты вершин треугольника назовем a, b, c

// Рассчитываем нормаль треугольника
float3 vNormal = SMVector3Normalize(SMVector3Cross(c - b, a - b));
// Проверяем, что треугольник подходит по направлению. unormal - нормаль желаемого направления декали
// Это необходимо, если мы хотим нарисовать декаль на тонкой двухсторонней поверхности, но при этом только с одной стороны
if(SMVector3Dot(vNormal, unormal) < -0.1f)
{
	continue; // отбрасываем треугольник
}

// Базисные векторы S, T, N (см изображение)
float3 vN = vNormal;
float3 vS, vT;

// пол/потолок
if(fabs(vNormal.y) > SIN_45_DEGREES)
{
	// T = S cross N
	vT = SMVector3Cross(float3(1.0f, 0.0f, 0.0f), vN);

	// S = N cross T
	vS = SMVector3Cross(vN, vT);
}
// стена
else
{
	// S = N cross T
	vS = SMVector3Cross(vN, float3(0.0f, -1.0f, 0.0f));
	// T = S cross N
	vT = SMVector3Cross(vS, vN);
}

vS = SMVector3Normalize(vS);
vT = SMVector3Normalize(vT);

SMMATRIX mBasis; // матрица базиса
SMMATRIX mBasisInv; // инверсная базисная матрица

mBasis.r[0] = float4(float3(vS), 0.0f);
mBasis.r[1] = float4(float3(vT), 0.0f);
mBasis.r[2] = float4(float3(vN), 0.0f);
mBasis.r[3] = float4(float3(position), 1.0f); // position - центр декали

// Инвертируем матрицу базиса
float det;
mBasisInv = SMMatrixInverse(&det, mBasis);

Декали в играх

3. Обрезка треугольника по границам декали

После того, как мы нашли базис в текстурном пространстве треугольника, необходимо обрезать нужную область:

Декали в играх

Array<float3> vClippedVerts, vClippedVerts2; // Временные массивы вертексов декали

// Переводим координаты вершин треугольника в новый базис и кладем в массив для обрезки
vClippedVerts.push_back(a * mBasisInv);
vClippedVerts.push_back(b * mBasisInv);
vClippedVerts.push_back(c * mBasisInv);

// Выполняем обрезку (код функции смотрите в репозитории движка в файле https://dev.ds-servers.com/sip/engine/blob/d3e3d8b2763a12095f83a47d959fcb6dd04bc637/source/decals/DecalManager.cpp#L198)
clip(vClippedVerts, vClippedVerts2, 's', sBound.y);
clip(vClippedVerts2, vClippedVerts, 's', sBound.x);
clip(vClippedVerts, vClippedVerts2, 't', tBound.y);
clip(vClippedVerts2, vClippedVerts, 't', tBound.x);

// Если в результате получилось меньше 3 вершин, этот треугольник отбрасываем
int len = vClippedVerts.size();
if(len < 3)
{
	continue;
}

Теперь дело за малым — сформировать набор треугольников из полученного набора вершин и рассчитать текстурные координаты. Не забываем, так же, вернуть наши координаты в пространство мира.

// Начальная вершина треугольника
vert0.pos = mBasis * vClippedVerts[0]; // Возвращаем в мировое пространство
vert0.normal = vNormal; // задаем оригинальную нормаль треугольника
vert0.tex = float2((vClippedVerts[0].x - sBound.x) / (sBound.y - sBound.x), (vClippedVerts[0].y - tBound.x) / (tBound.y - tBound.x));

// Формируем треугольник
for(int ii = 1; ii < len - 1; ii++)
{
	vDecalVerts.push_back(vert0);

	vert.pos = mBasis * vClippedVerts[ii];
	vert.normal = vNormal;
	vert.tex = float2((vClippedVerts[ii].x - sBound.x) / (sBound.y - sBound.x), (vClippedVerts[ii].y - tBound.x) / (tBound.y - tBound.x));
	vDecalVerts.push_back(vert);

	vert.pos = mBasis * vClippedVerts[ii + 1];
	vert.tex = float2((vClippedVerts[ii + 1].x - sBound.x) / (sBound.y - sBound.x), (vClippedVerts[ii + 1].y - tBound.x) / (tBound.y - tBound.x));
	vDecalVerts.push_back(vert);
}

Декали в играх

Отрисовка

Декаль готова, осталось ее отрисовать. Рендер происходит после всей непрозрачной геометрии с отключенной записью глубины и включенным альфа-блендингом.

// Включим альфаблендинг
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);

dev->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(DecalVertex));

// Установка материала
SGCore_MtlSet(iMaterialId, &SMMatrixIdentity());
dev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, iVertexCount / 3);

Декали в играх Декали в играх Декали в играх

Поделиться:

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

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

*