尝试在单个绘制指令中渲染多个动画(相同)3D模型。跨模型的动画和当前帧可能无法同步,例如一个人类模型可能正在运行,而另一个模型正在跳跃。
我为此选择的方法是由我正在使用的基础模型格式驱动的:wavefront,这对于动画效果不是很好。每个框架都存储为单独的独立模型。因此,为了实现我的目标,我仅将第一个帧用于渲染(也称为原始模型),然后构建一个纹理缓冲区,以保存每个顶点的帧0和每个帧的平移。
到目前为止,我已经实现了一半的目标:使用实例缓冲区渲染(glVertexAttribDivisor
和glDrawArraysInstanced
),我能够渲染一次帧0,但是可以多次转换以使其得以渲染在几个位置和方向(按实例矩阵输入)。
第二部分更加困难,引入了两个新的输入:
1.一个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...
正如评论中所解释的那样,预期结果是在不同位置有多个模型并被动画化,即遍历帧。
看到的结果确实是几个对象被变换,但是没有动画。
我在前面的评论中提到的故障可能指向该问题:
VS_FrameIndex
测试)第1点可能仍然是开放的,但我在这里没有介绍,因为在我看来VS_FrameIndex
的行为是要解决的第一个问题。
您可以从我粘贴的OpenGL / GLSL代码中看到任何错误吗?
编辑:
我在此问题上取得了一些进展,这证实了我在VS_FrameIndex
我的想法是:显然,实例化矩阵输入正在工作,并且帧ID是顶点着色器中唯一的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中的帧ID,但是通过这些更改,所有内容都可以像魅力一样工作……显然,这只是一个hack,在某个地方仍然存在错误找到。