计算机动画实验(四)OpenGL实现角色动画

实验题目来自2021年春季学期山东大学软件学院计算机动画基础课程
本人比较菜,代码有很多bug以及莫名其妙的地方,发在这记录一下写代码的艰辛,仅供参考思路哦!
现在代码已经找不到了,请不要找我要文件啦!(当然,欢迎指正)

  • 使用glfw,glad库,C++编写,参考LearnOpenGL

实验题目

给出人们见面时碰击手肘打招呼的角色动画
可以是图形方式,也可以是图像方式

思路

建立树形结构存储两个人物,其中分别存储身体、大臂、小臂对应的变换矩阵。
首先使用路径动画的方法使两个要打招呼的人慢慢走近。
其次,使用FK-正向运动学的方法确定两人胳膊各部分的位置,使其相交,以造成碰击手肘打招呼的效果。需要使用插值计算变换的中间位置。

实现效果

计算机动画实验(四)OpenGL实现角色动画_第1张图片计算机动画实验(四)OpenGL实现角色动画_第2张图片计算机动画实验(四)OpenGL实现角色动画_第3张图片

步骤

  1. 定义人物的数据结构,使用类实现。(比较粗糙,意思一下。。。)

    /*
    two people(manmask1,manmask2)knock the elbows
    one person devide into 3 parts -- body, upper arm, and lower arm
    use class man to describe
    */
    class man {
    public:
        glm::mat4 trans_body;
        glm::mat4 trans_upperarm;
        glm::mat4 trans_lowerarm;
    
        man();
        man(glm::mat4 tb, glm::mat4 tu, glm::mat4 tl) {
            trans_body = tb;
            trans_upperarm = tu;
            trans_lowerarm = tl;
        }
    };
    
  2. 初始化变换。

    t11 = glm::translate(t11,glm::vec3(tran11, 0.0f, 0.0f));
    t12 = glm::translate(t12, glm::vec3(tran12, 0.0f, 0.0f));
    t12 = glm::rotate(t12, glm::radians(rota12), glm::vec3(0.0, 0.0, 1.0));
    t13 = glm::translate(t13, glm::vec3(tran13, 0.0f, 0.0f));
    t13 = glm::rotate(t13, glm::radians(rota13), glm::vec3(0.0, 0.0, 1.0));
    
    t21 = glm::translate(t21, glm::vec3(tran21, 0.0f, 0.0f));
    t22 = glm::translate(t22, glm::vec3(tran22, 0.0f, 0.0f));
    t22 = glm::rotate(t22, glm::radians(rota22), glm::vec3(0.0, 0.0, 1.0));
    t23 = glm::translate(t23, glm::vec3(tran23, 0.0f, 0.0f));
    t23 = glm::rotate(t23, glm::radians(rota23), glm::vec3(0.0, 0.0, 1.0));
    
  3. 定义顶点数组以绘制人物以及其身体各部分,这里需要分开绘制,以便实现不同的变换。因为很难拟合身体各部分的边缘,因此这里需要使用png格式的图片,而png格式的图片因为存在透明部分,所以其顶点位置只需为正方形,因此绘制四个顶点就可以。

  4. 绑定vao和vbo,不同的身体部分需要绑定到不同的vao,以便在顶点着色器中实现不同的变换。

  5. 加载纹理,需要分为单独的人的身体、单独的大臂、单独的小臂。
    计算机动画实验(四)OpenGL实现角色动画_第4张图片计算机动画实验(四)OpenGL实现角色动画_第5张图片计算机动画实验(四)OpenGL实现角色动画_第6张图片

  6. 在渲染循环中不断改变各部分的旋转角度,即变换矩阵,并将其传给着色器,以实现碰击手肘的动画效果。(瞎写的,有点乱,慎看,能用就行。。。)

    if (i <= 66) {//change the people place
            tran11 += 0.0009;
            tran21 -= 0.0009;
    
            rota12 += 1.15 / 1.5;
            rota22 -= 2.3 / 1.5;
            if (rota13 < 95)
                rota13 += 2.4 / 1.5;
            if (rota23 > -70)
                rota23 -= 1.8 / 1.5;
        }
        else if (i>66 &&i <= 90) {//rotate while changing the people place
            rota12 += 1.15/1.5;
            rota22 -= 2.3/1.5;
            if(rota13 < 95)
                rota13 += 2.4/1.5;
            if(rota23 > -70)
                rota23 -= 1.8/1.5;
        }
        else if(i > 90){
            i = 0;
            tran11 = -0.16;
            tran21 = 0.16;
            rota12 = -10;
            rota22 = 23;
            rota13 = rota23 = 0;
        }
        t11 = glm::mat4(1.0f);
        t12 = glm::mat4(1.0f);
        t13 = glm::mat4(1.0f);
        t21 = glm::mat4(1.0f);
        t22 = glm::mat4(1.0f);
        t23 = glm::mat4(1.0f);
    
        //upper arms
        tran12 = -0.01;
        tran22 = 0.015;
        tran22_2 = -0.01;
        //lower arms
        tran13 = 0.08;
        tran13_2 = -0.074;
        tran23 = -0.0066;
        tran23_2 = -0.068;
    
        if (rota13 > 90) { //perform the clap 
            tran13 = 0.081;
            tran13_2 = -0.076;
            tran12 = -0.018;
        }
        if (rota23 < -68) {
            tran22 = 0.004;
            tran22_2 = -0.01;
        }
    
        t11 = glm::translate(t11, glm::vec3(tran11, 0.0f, 0.0f));
        t12 = glm::rotate(t12, glm::radians(rota12), glm::vec3(0.0, 0.0, 1.0));
        t12 = glm::translate(t12, glm::vec3(0.0f, tran12, 0.0f));
        t13 = glm::translate(t13, glm::vec3(0.0f, tran13_2, 0.0f));
        t13 = glm::translate(t13, glm::vec3(tran13, 0.0f, 0.0f));
        t13 = glm::rotate(t13, glm::radians(rota13), glm::vec3(0.0, 0.0, 1.0));
    
    
        t21 = glm::translate(t21, glm::vec3(tran21, 0.0f, 0.0f));
        t22 = glm::translate(t22, glm::vec3(tran22, 0.0f, 0.0f));
        t22 = glm::translate(t22, glm::vec3( 0.0f,tran22_2, 0.0f));
        t22 = glm::rotate(t22, glm::radians(rota22), glm::vec3(0.0, 0.0, 1.0));
        t23 = glm::translate(t23, glm::vec3(tran23, 0.0f, 0.0f));
        t23 = glm::translate(t23, glm::vec3( 0.0f,tran23_2, 0.0f));
        t23 = glm::rotate(t23, glm::radians(rota23), glm::vec3(0.0, 0.0, 1.0));
    
        man1 = man(t11, t12, t13);
        man2 = man(t21, t22, t23);//build two men
    
        //background!
        // activate shader
        ourShader.use();
        // create transformations
        glm::mat4 model1 = glm::mat4(1.0f);
        glm::mat4 view1 = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
        glm::mat4 projection1 = glm::mat4(1.0f);
        projection1 = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        view1 = glm::translate(view1, glm::vec3(-0.3f, 0.0f, -3.0f));
        view1 = glm::scale(view1, glm::vec3(2, 2, 2));
    
        // pass transformation matrices to the shader
        ourShader.setMat4("projection1", projection1); /        ourShader.setMat4("view1", view1);
        ourShader.setMat4("model1", model1);
        glBindVertexArray(VAO_bg);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
        //body 1 !
        // activate shader
        ourShader1.use();
        // create transformations
        glm::mat4 model2 = glm::mat4(1.0f);
        glm::mat4 view2 = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
        glm::mat4 projection2 = glm::mat4(1.0f);
        projection2 = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        ourShader1.setMat4("projection1", projection2);         ourShader1.setMat4("model1", model2);
        //body
        view2 = view2 * man1.trans_body;
        ourShader1.setMat4("view1", view2);
        glBindVertexArray(VAO_pp11);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
        //upper  arm 1 !
        // activate shader
        ourShader2.use();
        glBindVertexArray(VAO_pp12);
        ourShader2.setMat4("projection1", projection2);         ourShader2.setMat4("model1", model2);
        //upperarm
        view2 = view2 * man1.trans_upperarm;
        ourShader2.setMat4("view1", view2);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);      
    
        //lower  arm 1 !
        // activate shader
        ourShader3.use();
        glBindVertexArray(VAO_pp13);
        ourShader3.setMat4("projection1", projection2);         ourShader3.setMat4("model1", model2);
        //lowerarm
        view2 = view2 * man1.trans_lowerarm;
        ourShader3.setMat4("view1", view2);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    

结论分析与体会

  1. 角色动画的基本思路并不复杂,主要有两个比较关键的部分,一个是树形数据结构的构建,另一个是使用IK或FK的运动学方法求解位置变换信息。
    树形结构的构建:
    弧对应关节,节点对应链杆。
    节点,包含:
    1)关节链对象数据
    2)该对象数据的变换信息,使其能够绕自身原点旋转(非必须)
    弧,包含:
    1)用来描述关节链之间的相对位置信息
    2)关节链实际运动的变换信息
    计算机动画实验(四)OpenGL实现角色动画_第7张图片计算机动画实验(四)OpenGL实现角色动画_第8张图片

    对于本实验,因为人物身体的其他部分没有发生移动,因此可以对该数据结构进行适当的简化。
    使用IK或FK的运动学方法求解位置变换信息:

    • FK-正向运动学的计算方法思路比较简单,直接由程序员从根节点开始逐个设置每一个部件的变换信息,需要有一定的经验,同时也可能需要多次进行调试才能达到满意的效果。
    • IK-逆向运动学的方法比较直观,只需要由程序员定义末端影响器的位置,电脑即可自动算出其他部件的位置与变换。但是运算比较复杂,可能需要牛顿迭代法等算法才能求解。
    • 本次实验因为需要移动的胳膊部分部件较少,因此使用正向运动学的方法直接求解。(其实是逆向不太会啦)
  2. 在使用正向运动学求解各部件位置时,要格外注意变换处于的坐标系。可以理解为对之前部件的每一次变换,都相当于也作用到了坐标系上。

一点不重要的

  • 再次感谢某位同学一直以来的帮助!

你可能感兴趣的:(动画,c++)