对码当歌,猿生几何?

FBX格式解析顶点信息和用OpenGL显示

FbxSDK是解析FBX的工具,在unity和ue中使用较多,下面介绍一下用这个工具解析FBX格式并用OpenGL显示出来。

FBX的scene是由一系列node组成的,node包含一个Transfrom和NodeAttribute,NodeAttribute包含很多类型,如eMaker、eSkeleton、eMaker、eMesh等。

模型的顶点信息存放在eMesh里,eMesh中包含了控制点信息和索引信息。索引信息指的是一系列index,如1个三角形由控制点的第0,1,2这三个点组成一个三角形,则索引信息存放这3个索引数组。

控制点 信息可以由GetControlPoints方法得到,而索引信息可以由GetPolygonVertex得到。

接下来用一个数组存放这些所有的控制点和索引号,定义如下数据结构:

struct Point{
    GLfloat x;
    GLfloat y;
    GLfloat z;
};

int TriangleVertexCount=0;
int TriangleIndexCount=0;

std::vector<Point> triangleVertexVec;         //控制点列表
std::vector<int> triangleIndexVec;              //索引序列

由于模型中的mesh不止一个,放在一起需要重新编号:

void readMesh(FbxMesh *mesh)
{
    int ctrlcount=mesh->GetControlPointsCount();
    FbxVector4 *ctrlpoints=mesh->GetControlPoints();
    for(int i=0;i<ctrlcount;i++)
    {
        Point p;
        p.x=ctrlpoints[i].mData[0];
        p.y=ctrlpoints[i].mData[1];
        p.z=ctrlpoints[i].mData[2];
        triangleVertexVec.push_back(p);
    }
    int pvcount=mesh->GetPolygonVertexCount();
    int pcount=mesh->GetPolygonCount();
    for(int i=0;i<pcount;i++)
    {
        int psize=mesh->GetPolygonSize(i);
        for(int j=0;j<psize;j++)
            triangleIndexVec.push_back(TriangleIndexCount+mesh->GetPolygonVertex(i,j));
    }
    TriangleVertexCount+=ctrlcount;          //加上上一个node的控制点个数
    TriangleIndexCount+=ctrlcount;
}

遍历所有node的函数如下:

void parseNode(FbxNode *node)              //如何加载fbx文件获得RootNode此处省略
{
    int count=node->GetChildCount();
    parseNodeAttribute(node);
    
    for(int i=0;i<count;i++)
    {
        FbxNode* son=node->GetChild(i);
        parseNode(node->GetChild(i));
    }
}

void parseNodeAttribute(FbxNode *node)
{
    int nacount=node->GetNodeAttributeCount();
    for(int i=0;i<nacount;i++)
    {
        FbxNodeAttribute *na=node->GetNodeAttributeByIndex(i);
        if(na->GetAttributeType()==FbxNodeAttribute::eMesh)
        {
            FbxMesh *mesh=(FbxMesh*)na;
            readMesh(mesh);
        }
    }

至此所有的mesh中的控制点和索引已经存放到triangleVertexVec和triangleIndexVec中,只需要将这个三角形面放入到OpenGL里实现就可以了。初始化OpenGL,编译shader的函数如下:

void init(void)
{
    ShaderInfo shaders[] = {
        { GL_VERTEX_SHADER, "primitive_restart.vs.glsl" },
        { GL_FRAGMENT_SHADER, "primitive_restart.fs.glsl" },
        { GL_NONE, NULL }
    };
    program=LoadShaders(shaders);         //此处用了OpenGL红宝书中的LoadShaders
    glUseProgram(program);
    glClearColor(0,0,0,1);
    glGenVertexArrays(4,voa);
    glBindVertexArray(voa[0]);
    glGenBuffers(4, vob);
    glBindBuffer(GL_ARRAY_BUFFER, vob[0]);
    triangleVertex=new GLfloat[triangleVertexVec.size()*3];
    for(int i=0;i<triangleVertexVec.size();i++)
    {
        triangleVertex[3*i]=triangleVertexVec[i].x;
        triangleVertex[3*i+1]=triangleVertexVec[i].y;
        triangleVertex[3*i+2]=triangleVertexVec[i].z;
    }
    triangleIndex=new GLuint[triangleIndexVec.size()*3];
    for(int i=0;i<triangleIndexVec.size();i++)
    {
        triangleIndex[i]=triangleIndexVec[i];
    }
    glBufferData(GL_ARRAY_BUFFER, triangleVertexVec.size()*3*sizeof(GLdouble), triangleVertex, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const void *)(0));
    glEnableVertexAttribArray(0);
    delete triangleVertex;
    delete triangleIndex;
}

绘制的函数如下:

void display()
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    GLuint t_index=glGetUniformLocation(program,"model_transform");
    vmath::mat4 transformMatrix=vmath::scale<float>(0.006);
    glUniformMatrix4fv(t_index,1,GL_FALSE,transformMatrix);
    glDrawElements(GL_TRIANGLES,triangleIndexVec.size(),GL_UNSIGNED_INT,triangleIndex);
    glFlush();
}

顶点着色器的代码为:

#version 400

layout (location = 0) in vec4 position;
uniform mat4 model_transform;

void main(void)
{
    
    gl_Position = model_transform*position;
}
片元着色器代码为:

#version 400
layout (location = 0) out vec4 color;


void main(void)
{
    color = vec4(1.0,0.0,0.0,1.0);
}

绘制出来的效果为:

虽然有点丑,但是还是能看出效果的哈。。。