实验题目来自2021年春季学期山东大学软件学院计算机动画基础课程
本人比较菜,代码有很多bug以及莫名其妙的地方,发在这记录一下写代码的艰辛,仅供参考思路哦!
现在代码已经找不到了,请不要找我要文件啦!(当然,欢迎指正)
给出人们见面时碰击手肘打招呼的角色动画
可以是图形方式,也可以是图像方式
建立树形结构存储两个人物,其中分别存储身体、大臂、小臂对应的变换矩阵。
首先使用路径动画的方法使两个要打招呼的人慢慢走近。
其次,使用FK-正向运动学的方法确定两人胳膊各部分的位置,使其相交,以造成碰击手肘打招呼的效果。需要使用插值计算变换的中间位置。
定义人物的数据结构,使用类实现。(比较粗糙,意思一下。。。)
/*
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;
}
};
初始化变换。
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));
定义顶点数组以绘制人物以及其身体各部分,这里需要分开绘制,以便实现不同的变换。因为很难拟合身体各部分的边缘,因此这里需要使用png格式的图片,而png格式的图片因为存在透明部分,所以其顶点位置只需为正方形,因此绘制四个顶点就可以。
绑定vao和vbo,不同的身体部分需要绑定到不同的vao,以便在顶点着色器中实现不同的变换。
在渲染循环中不断改变各部分的旋转角度,即变换矩阵,并将其传给着色器,以实现碰击手肘的动画效果。(瞎写的,有点乱,慎看,能用就行。。。)
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);
角色动画的基本思路并不复杂,主要有两个比较关键的部分,一个是树形数据结构的构建,另一个是使用IK或FK的运动学方法求解位置变换信息。
树形结构的构建:
弧对应关节,节点对应链杆。
节点,包含:
1)关节链对象数据
2)该对象数据的变换信息,使其能够绕自身原点旋转(非必须)
弧,包含:
1)用来描述关节链之间的相对位置信息
2)关节链实际运动的变换信息
对于本实验,因为人物身体的其他部分没有发生移动,因此可以对该数据结构进行适当的简化。
使用IK或FK的运动学方法求解位置变换信息:
在使用正向运动学求解各部件位置时,要格外注意变换处于的坐标系。可以理解为对之前部件的每一次变换,都相当于也作用到了坐标系上。