Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #64, МАРТ 2006 г.

проблемная рожа

А.ГЛАДЫШ, А.ФЕДОРА

Спецвыпуск: Хакер, номер #064, стр. 064-058-4


Итак, положение заданного вертекса меша модели в конечном кадре вычисляется так: суммируем положения этого вертекса в каждом канале, взяв их с весами, заданными для данного момента времени:

pos = pos1 * (1 — weight2 — weight3) + pos2 * weight2 + pos3 * weight3.

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

Листинг

полный код вертексного шейдера на HLSL для морфирующей анимации из трех каналов (без работы с текстурами)

// Матрица преобразования.

float4x4 WorldViewProjection : WORLDVIEWPROJECTION;

float3 weight; // Веса анимаций

// weight.x — вес основного кадра

// weight.y — вес кадра «открытый рот»

// weight.z — вес кадра с эмоцией

// Вершинный шейдер

void vshader(

float4 vPos0 : POSITION0, // вершина из первого потока

float3 vNormal0 : NORMAL0, // нормаль из первого потока

float4 vPos1 : POSITION1, // вершины из второго потока

float3 vNormal1 : NORMAL1, // нормаль из второго потока

float4 vPos2 : POSITION2, // вершины из третьего потока

float3 vNormal2 : NORMAL2, // нормаль из третьего потока

out float4 oPosition : POSITION, // результирующая позиция вершины

out float4 oDiffuse : COLOR0 // цвет вершины

)

{

// Здесь смешиваются три кадра анимации

// в соответствии с заданными весами

// Позиция:

float4 cPos = vPos0 * weight.x +

vPos1 * weight.y +

vPos2 * weight.z;

// Нормаль:

float3 cNor = normalize(

vNormal0 * weight.x +

vNormal1 * weight.y +

vNormal2 * weight.z

);

// трансформируем полученную позицию

// матрицей преобразования.

oPosition = mul( cPos, WorldViewProjection );

// Расчет примитивного освещения

float3 lightPos = float3(0, 0, 100);

float3 invL = normalize(lightPos — cPos);

float NdotL = dot(cNor,invL);

oDiffuse.xyz = NdotL + 0.2;

oDiffuse.w = 0;

}

под свой формат

В примере используются кадры для морфинга, которые были экспортированы из 3D Studio Max в собственный упрощенный текстовый формат с помощью плагина, специально написанного на MАХScript. В реальной ситуации, скорее всего, ты захочешь применить уже используемый формат данных. Помни, что ты должен обеспечить одинаковый порядок вертексов в каждом кадре модели.

в итоге

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

Назад на стр. 064-058-3  Содержание  Вперед на стр. 064-058-5