Несколько входов в инстансированный массив-буфер не полностью / частично отправлены в вершинный шейдер - PullRequest
0 голосов
/ 06 февраля 2019

Попытка визуализации в одной инструкции рисования нескольких анимированных (идентичных) 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...

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

В результате видно, что несколько объектов преобразованы, но не анимированы.

Глюки, которые я упоминал ранее в комментариях, могут указывать наПроблема:

  1. Либо в том, как я строю свои трансляционные данные для каждого кадра
  2. , либо в том, как я их получаю (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, но с этими изменениями все работает как чудо ... Очевидно, это просто взлом, и до сих порошибку где-то можно найти.

...