qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 09:03:22 浏览: 563
下面是使用 Qt、OpenGL 和 Assimp 库加载骨骼动画的完整代码示例。在这个示例中,我们使用 Assimp 库加载一个包含骨骼动画的模型,并在 OpenGL 中渲染它。
```c++
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QMatrix4x4>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
GLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {}
protected:
void initializeGL() override {
initializeOpenGLFunctions();
// 编译顶点着色器和片段着色器
m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader.vert");
m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader.frag");
m_program.link();
// 加载模型
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile("model.dae", aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices | aiProcess_CalcTangentSpace | aiProcess_LimitBoneWeights);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
qWarning() << "Failed to load model:" << importer.GetErrorString();
return;
}
// 初始化骨骼动画
m_boneMatrices.resize(scene->mNumMeshes);
m_boneOffsets.resize(scene->mNumMeshes);
m_boneMapping.resize(scene->mNumMeshes);
for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[i];
// 计算骨骼权重
QVector<QVector<QPair<int, float>>> boneWeights(mesh->mNumVertices);
for (unsigned int j = 0; j < mesh->mNumBones; j++) {
aiBone* bone = mesh->mBones[j];
int boneIndex = m_boneMapping[i].value(QString::fromUtf8(bone->mName.C_Str()), -1);
if (boneIndex == -1) {
boneIndex = m_boneOffsets[i].size();
m_boneOffsets[i].append(QMatrix4x4());
m_boneMapping[i].insert(QString::fromUtf8(bone->mName.C_Str()), boneIndex);
}
m_boneOffsets[i][boneIndex] = QMatrix4x4((float*)bone->mOffsetMatrix.Transpose().a1);
for (unsigned int k = 0; k < bone->mNumWeights; k++) {
aiVertexWeight weight = bone->mWeights[k];
boneWeights[weight.mVertexId].append(qMakePair(boneIndex, weight.mWeight));
}
}
// 计算每个顶点的骨骼矩阵
QVector<QVector<QMatrix4x4>> boneMatrices(mesh->mNumVertices);
for (int j = 0; j < mesh->mNumVertices; j++) {
QVector<QPair<int, float>> weights = boneWeights[j];
for (int k = 0; k < weights.size(); k++) {
int boneIndex = weights[k].first;
float weight = weights[k].second;
boneMatrices[j].append(weight * m_boneOffsets[i][boneIndex]);
}
}
// 上传骨骼矩阵到 GPU
int boneMatrixLocation = m_program.attributeLocation(QString("boneMatrix[%1]").arg(i));
for (int j = 0; j < boneMatrices.size(); j++) {
for (int k = 0; k < 4; k++) {
for (int l = 0; l < 4; l++) {
m_boneMatrices[i][j].data()[k * 4 + l] = boneMatrices[j][k][l];
}
}
}
m_program.setAttributeArray(boneMatrixLocation, GL_FLOAT_MAT4, m_boneMatrices[i].constData(), 4);
m_program.enableAttributeArray(boneMatrixLocation);
}
// 上传顶点数据到 GPU
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[i];
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (3 + 3 + 2 + 4 * 4) * mesh->mNumVertices, nullptr, GL_STATIC_DRAW);
float* data = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
for (unsigned int j = 0; j < mesh->mNumVertices; j++) {
aiVector3D position = mesh->mVertices[j];
aiVector3D normal = mesh->mNormals[j];
aiVector3D texcoord = mesh->mTextureCoords[0][j];
for (int k = 0; k < 3; k++) {
*data++ = position[k];
}
for (int k = 0; k < 3; k++) {
*data++ = normal[k];
}
for (int k = 0; k < 2; k++) {
*data++ = texcoord[k];
}
for (int k = 0; k < m_boneMatrices[i][j].size(); k++) {
for (int l = 0; l < 4; l++) {
for (int m = 0; m < 4; m++) {
*data++ = m_boneMatrices[i][j](l, m);
}
}
}
}
glUnmapBuffer(GL_ARRAY_BUFFER);
int positionLocation = m_program.attributeLocation("position");
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(float) * (3 + 3 + 2 + 4 * 4), (void*)0);
glEnableVertexAttribArray(positionLocation);
int normalLocation = m_program.attributeLocation("normal");
glVertexAttribPointer(normalLocation, 3, GL_FLOAT, GL_FALSE, sizeof(float) * (3 + 3 + 2 + 4 * 4), (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(normalLocation);
int texcoordLocation = m_program.attributeLocation("texcoord");
glVertexAttribPointer(texcoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(float) * (3 + 3 + 2 + 4 * 4), (void*)(sizeof(float) * 6));
glEnableVertexAttribArray(texcoordLocation);
int boneMatrixLocation = m_program.attributeLocation(QString("boneMatrix[%1]").arg(i));
for (int j = 0; j < 4; j++) {
glVertexAttribPointer(boneMatrixLocation + j, 4, GL_FLOAT, GL_FALSE, sizeof(float) * (3 + 3 + 2 + 4 * 4), (void*)(sizeof(float) * (8 + j * 4)));
glEnableVertexAttribArray(boneMatrixLocation + j);
glVertexAttribDivisor(boneMatrixLocation + j, 1);
}
}
// 加载纹理
QImage image("texture.png");
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
// 开启深度测试和面剔除
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
void paintGL() override {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 计算相机矩阵和投影矩阵
QMatrix4x4 cameraMatrix;
cameraMatrix.translate(0, 0, -10);
QMatrix4x4 projectionMatrix;
projectionMatrix.perspective(45, (float)width() / height(), 0.1f, 100.0f);
// 绘制模型
m_program.bind();
m_program.setUniformValue("cameraMatrix", cameraMatrix);
m_program.setUniformValue("projectionMatrix", projectionMatrix);
glBindTexture(GL_TEXTURE_2D, m_texture);
glBindVertexArray(m_vao);
for (unsigned int i = 0; i < m_boneMatrices.size(); i++) {
m_program.setUniformValue(QString("boneMatrix[%1]").arg(i), m_boneMatrices[i]);
}
for (unsigned int i = 0; i < m_boneMatrices.size(); i++) {
aiMesh* mesh = scene->mMeshes[i];
glDrawArrays(GL_TRIANGLES, 0, mesh->mNumVertices);
}
}
private:
QOpenGLShaderProgram m_program;
GLuint m_vao;
GLuint m_texture;
QVector<QMatrix4x4> m_boneMatrices;
QVector<QVector<QMatrix4x4>> m_boneOffsets;
QVector<QHash<QString, int>> m_boneMapping;
};
```
注:这个代码示例中使用 `m_program` 对象表示 OpenGL 着色器程序,`m_vao` 表示 OpenGL 顶点数组对象,`m_texture` 表示 OpenGL 纹理对象,`m_boneMatrices` 表示骨骼矩阵,`m_boneOffsets` 表示骨骼偏移矩阵,`m_boneMapping` 表示骨骼名称到骨骼索引的映射。其中 `:/shader.vert` 和 `:/shader.frag` 是着色器程序的代码文件,`model.dae` 是包含骨骼动画的模型文件,`texture.png` 是纹理文件。
阅读全文