Попытка визуализации в одной инструкции рисования нескольких анимированных (идентичных) 3D-моделей.Анимация и текущие кадры в разных моделях могут быть не синхронизированы, например, человеческая модель может работать, а другая - прыгает.
Подход, который я выбрал для этого, определяется форматом базовой модели, с которой я работаю: wavefront,что не очень хорошо с анимацией.Каждый кадр хранится как отдельная и независимая модель.Чтобы достичь своей цели, я, таким образом, использую первый кадр только для рендеринга (он же оригинальная модель), а затем создаю буфер текстур, содержащий перевод из кадра 0 каждой вершины и для каждого кадра.
До сих пор я достигполовина моих целей: с помощью рендеринга экземпляра в буфере (glVertexAttribDivisor
и glDrawArraysInstanced
) я могу рендерить кадр 0 один раз, но трансформироваться несколько раз, чтобы он отображался в нескольких местах и в одной ориентации (для входных данных матрицы каждого экземпляра).
Вторая часть оказывается более сложной, вводятся два новых входа: 1. A samplerBuffer
(glTexBuffer
), содержащий векторы трансляции FramesCount x VerticePerFrame 2. Ввод данных второго экземпляра: индекс текущего кадра вмодель
// --------------------------------------------------------------
// Preparing my rendering buffers...
// This is working well, at least as far as the model rendering and dynamic
// positioning goes ("transfsLocation" input)
// frameIdLocation is the one I'm unsure of
buffer_ids = std::vector<GLuint>(3, 0);
glGenBuffers(3, &buffer_ids[0]);
glBindBuffer(GL_ARRAY_BUFFER, buffer_ids[0]);
glBufferData(GL_ARRAY_BUFFER, vsize * sizeof(bump_t), &data[0], GL_STATIC_DRAW);
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);
{
glBindBuffer(GL_ARRAY_BUFFER, buffer_ids[0]);
glVertexAttribPointer(vpointsLocation, 3, GL_FLOAT, GL_FALSE, sizeof(bump_t), (void*)(0));
glVertexAttribPointer(normalsLocation, 3, GL_FLOAT, GL_FALSE, sizeof(bump_t), (void*)(sizeof(glm::vec3)));
glVertexAttribPointer(tcoordsLocation, 2, GL_FLOAT, GL_FALSE, sizeof(bump_t), (void*)(sizeof(glm::vec3) + sizeof(glm::vec3)));
glVertexAttribPointer(tangentLocation, 3, GL_FLOAT, GL_FALSE, sizeof(bump_t), (void*)(sizeof(glm::vec3) + sizeof(glm::vec3) + sizeof(glm::vec2)));
glVertexAttribPointer(coloursLocation, 4, GL_FLOAT, GL_FALSE, sizeof(bump_t), (void*)(sizeof(glm::vec3) + sizeof(glm::vec3) + sizeof(glm::vec2) + sizeof(glm::vec3)));
glEnableVertexAttribArray(vpointsLocation);
glEnableVertexAttribArray(normalsLocation);
glEnableVertexAttribArray(tcoordsLocation);
glEnableVertexAttribArray(tangentLocation);
glEnableVertexAttribArray(coloursLocation);
// Dynamic buffer of integers (frame indexes). Note the GL_INT type, which I am assuming is the appropriate one here?
glBindBuffer(GL_ARRAY_BUFFER, buffer_ids[1]);
glEnableVertexAttribArray(frameIdLocation);
glVertexAttribPointer(frameIdLocation, 1, GL_INT, GL_FALSE, 0, 0);
glVertexAttribDivisor(frameIdLocation, 1);
// Dynamic buffer for 4x4 matrices
glBindBuffer(GL_ARRAY_BUFFER, buffer_ids[2]);
for (auto i = 0; i < 4; ++i)
{
glEnableVertexAttribArray(transfsLocation + i);
glVertexAttribPointer(transfsLocation + i, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (const void*)(sizeof(float) * 4 * i));
glVertexAttribDivisor(transfsLocation + i, 1);
}
}
glBindVertexArray(0);
// --------------------------------------------------------------
// Preparing the frame transformations texture
std::vector<glm::vec3> framesTransforms;
// Filling framesTransforms with something like this,
// Where each vec3 is a translation of a vertex from the original frame 0 position
framesTransforms = {
vec3(), vec3(), vec3(), vec3(), vec3(), // End of frame 0
vec3(), vec3(), vec3(), vec3(), vec3(), // End of frame 1
...
};
const size_t size = framesTransforms.size() * sizeof(glm::vec3);
glGenBuffers(1, &frameBufferId);
glBindBuffer(GL_TEXTURE_BUFFER, frameBufferId);
glBufferData(GL_TEXTURE_BUFFER, size, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_TEXTURE_BUFFER, 0, size, &framesTransforms[0]);
glGenTextures(1, &frameTextureId);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_BUFFER, frameTextureId);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, frameBufferId);
// --------------------------------------------------------------
// And now rendering...
// Of course frameIdx.size() == transforms.size()
std::vector<glm::mat4> transforms;
std::vector<int> frameIdx;
// frameIdx is updated regularily with the next frame for each model
// something like:
// for each: frameIdx[ ... ] = (frameIdx[ ... ] + 1) % NumberOfFrames;
glBindBuffer(GL_ARRAY_BUFFER, buffer_ids[buffer_ids.size() - 2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(int) * frameIdx.size(), &frameIdx[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buffer_ids[buffer_ids.size() - 1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * transforms.size(), &transforms[0], GL_DYNAMIC_DRAW);
glBindVertexArray(vertexArray);
glDrawArraysInstanced(GL_TRIANGLES, 0, data.size(), transforms.size());
glBindVertexArray(0);
// --------------------------------------------------------------
// And now on the vertex shader side
// All the inputs we saw earlier
in vec3 VS_Position;
in vec4 VS_Color;
in vec2 VS_TexCoord;
in vec3 VS_Normal;
in vec3 VS_Tangent;
in mat4 VS_Transform;
in int VS_FrameIndex;
// Frame data sampler: note that I didn't show the C++ setting of those,
// but I was able to test that they are being set correctly
// (at least FrameSampler is, as I will explain later)
uniform samplerBuffer FrameSampler;
uniform int VertexPerFrame;
// Now we can apply the per instance transformations:
vec3 frameTransform = texelFetch(FrameSampler, VertexPerFrame * VS_FrameIndex + gl_VertexID).xyz;
vec3 position = VS_Position + frameTransform
// P, V and W are of course other uniforms we don't need to explain here :)
gl_Position = (P * V * W) * (VS_Transform * vec4(position, 1.0));
// -> The outcome is several models at different locations/rotations (the effect of VS_Transform), but --mostly-- immobile i.e. VS_FrameIndex or the vertex-translation fetching not working.
// Note that I do see glitches from time to time, where the model gets sort of dislocated for a split second, and then back to normal
// Here is a little test I ran to prove that I was able to read data from my sampler:
// TEST 1
// Modifying the code above as follows:
// On C++ side, the frame texture is filled with very small/simple data:
framesTransforms = { glm::vec3(0, 0, 0), glm::vec3(0, 3, 0), glm::vec3(0, 6, 0) };
// And assuming we are rendering 3 models, the vertex shader now does:
vec3 frameTransform = texelFetch(FrameSampler, gl_InstanceID).xyz;
// The outcome is indeed 3 models whos respective Y-position is 0, 3 and 6,
// thus showing that my sampler holds the data I fed in, and I am able to read from it.
// TEST 2
// Using the same data setup for framesTransforms
// And making sure VS_FrameIndex is within [0,2]
vec3 frameTransform = texelFetch(FrameSampler, VS_FrameIndex).xyz;
// With this, I don't see my 3 models at different heights...
// -> my issue seems to be connected to the dynamic input "VS_FrameIndex" itself...
Как поясняется в комментариях, ожидаемые результаты - это несколько моделей в разных местах, которые анимированы, т. е. проходят через кадры.
В результате видно, что несколько объектов преобразованы, но не анимированы.
Глюки, которые я упоминал ранее в комментариях, могут указывать наПроблема:
- Либо в том, как я строю свои трансляционные данные для каждого кадра
- , либо в том, как я их получаю (cf test с
VS_FrameIndex
)
Точка 1. все еще может быть открыта, но я не рассмотрел ее здесь, потому что мне кажется, что поведение VS_FrameIndex
является первой проблемой, которую нужно решить.
Можете ли вы увидеть что-нибудьнеправильно из вставленного мной кода OpenGL / GLSL?
РЕДАКТИРОВАТЬ: Я достиг определенного прогресса в этом вопросе, который подтверждает, что есть проблема с моей настройкой и / или использованием идентификаторов кадров в VS_FrameIndex
Моя идея заключалась в том, что, по-видимому, работает экземпляр инстанцированных матриц, а идентификаторы кадров являются единственными int
входами в вершинном шейдере.Поэтому я попытался использовать vec3
вместо:
in int VS_FrameIndex;
vec3 frameTransform = texelFetch(FrameSampler, VertexPerFrame * VS_FrameIndex + gl_VertexID).xyz;
Теперь становится:
in vec3 VS_FrameIndex;
vec3 frameTransform = texelFetch(FrameSampler, VertexPerFrame * int(VS_FrameIndex.x) + gl_VertexID).xyz;
И на стороне инициализации:
glVertexAttribPointer(frameIdLocation, 1, GL_INT, GL_FALSE, 0, 0);
Изменения:
glVertexAttribPointer(frameIdLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
Конечно, мне также пришлось настроить C ++ для игры с идентификаторами фреймов в vec3, но с этими изменениями все работает как чудо ... Очевидно, это просто взлом, и до сих порошибку где-то можно найти.