Морфирующая анимация при помощи шейдеров GLSL и програмным способом.

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





К примеру, в программе на OpenGL для морфирующей анимации необохдимо три буфера вершин – первый буфер вершин для модели куба, второй буфер вершин для модели сферы, третий буфер вершин для конечного объекта, который будет выводится на экран. В этот третий буфер вершин заносятся данные интерполяции между вершинами куба и сферы. Можно на простом примере продемонстрировать смысл интерполяции. Возьмем два значения 15 и 25, и возьмем значение скаляра от 0 до 1, в нашем случае 0.5. Что бы проделать интерполяцию, нам нужно значение скляра взятое от 0 до 1 наложить на диапазон от 15 до 25.

25 – 15 = 10
10 * 0.5 = 5
15 + 5 = 20

в примере, если на диапазон от 15 до 25 наложить значение скаляра 0.5, то результат интерполяции будет 20. Другой пример, на диапазон от 15 до 25 наложить значение скаляра 0.7.

25 – 15 = 10
10 * 0.7 = 7
15 + 7 = 22

результат будет значение 22. В примере с кубом и сферой, третий буфер вершин содержит резльутат интерполяции, когда значение скаляра равно 0 то третий буфер вершин содержит модель куба, а когда значение скаляра равно 1, третий буфер вершин содержит модель сферы, и на экран выводится изображение сферы. В прогрмме необходимо предусмотреть, что бы значение скаляра менялось от 0 до 1 и обратно, с течением времени, и затем это значение скаляра накладывать на соответсвтующие вершины куба и сферы, что бы получить результат – интерполированную вершину.
В другом виде ту же интерполяцию можно сделать так, взяв скаляр 0.7:

15 * (1.0 - 0.7) = 4.5
25 * 0.7 = 17.5
4.5 + 17.5 = 22

Этот подход использовался в примере вершинного шейдера, который приведен ниже. Сначала на вход шейдера подаются два буфера вершин VertexPosition_1 для модели куба, VertexPosition_2 для модели сферы. Так же на вход шейдера подается значение скаляра fScalar от 0 до 1. В функции main производится интеполяция вершин в зависимости от величины скаляра, и результат интерполяции будет далее использован для вывода результирующей модели на экран.

layout (location = 0) in vec3 VertexPosition_1;
layout (location = 1) in vec3 VertexPosition_2;
layout (location = 2) in vec3 VertexColor;

out vec3 Color;


uniform mat4 ViewMatrix;
uniform mat4 ProjMatrix;
uniform float fScalar;


void main()
{
Color = VertexColor;

vec3 vSrc = VertexPosition_1 * (1.0 - fScalar);
vec3 vDst = VertexPosition_2 * fScalar;
vec3 VertexPosition = vSrc + vDst;

gl_Position = ProjMatrix * ViewMatrix * vec4(VertexPosition,1.0);

}

Что бы в программе на С++ задавать значение скаляра, можно использовать в приложении собственный класс таймера, к примеру взять время, прошедшее от начала запуска приложения, сделать преобразования, а затем подать на вход шейдера, причем этим же значением скаляра можно задавать скорость анимации:

float fAppTime = mTimer.GetAppTime();
FLOAT fKickFreq = fAppTime * 2.0f;
FLOAT fBlendWeight = sinf( fKickFreq );
fBlendWeight = 0.5f + fBlendWeight/2;

GLuint Scalar = glGetUniformLocation(programHandle, "fScalar");
if( Scalar >= 0 )
{
glUniform1f(Scalar, (GLfloat)fBlendWeight);
}


В случае, если морфинг программный, то есть без использования шейдеров, то программа должна иметь такую последовательность: сначала создать два буфера вершин с моделями куба и сферы соответственно, далее написать код который будет генерировать значение скаляра от 0 до 1, в каждом кадре вызывать функцию Update() в которую передавать значение скаляра, внутри функции Update() в зависимости от величины скаляра будет заполнятся третий результирующий буфер вершин, который затем выводится на экран.

void CMeshManager::Update(float Scalar)
{
for( DWORD i=0; i < CubeModel.nVertCount; i++ )
{

ogl_vector vSrcP = CubeModel.pVertsArray[i];
ogl_vector vDstP = SphereModel.pVertsArray[i];

vSrcP= vSrcP* (float)(1.0 - Scalar);
vDstP= vDstP* Scalar;
ResModel.pVertsArray[i] = vSrcP+vDstP;
}
}

в данном примере внутри функции Update используется два буфера вершин, один для куба CubeModel.pVertsArray и другой для сферы SphereModel.pVertsArray, производится интерполяция в зависимости от величины Scalar переменной. Результат интерполяции заносится в третий буфер ResModel.pVertsArray который далее в функции RenderScene() выводится на экран при помощи функции glDrawElements(). Как ясно количество вершин модели куба и сферы должно равнятся, в функции выше все вершины обеих моделей в цикле интерполируются, за основу цикла взято количество вершин модели куба CubeModel.nVertCount().