老婆镇楼!白毛真是prpr!
一般现代OpenGL指的是用OpenGL3.3左右+GLM+GLAD+GLFW/SDL/etc.
矩阵操作更加直观,这里点名批评GLUT,代码简直不是人看的
本文改编网上glut的源码到glad-sdl/glfw
为后面操作骨骼做准备!
准备顶点数据,这里提供aPos,aNormal, indices
填充到mesh类,mesh类可以到learnopengl下载
本代码提供了box,Pyramid等类型的显示,数据由Maya建模获取,原理就是做个默认cube的内接几何体,然后放大100倍,也就是外接一个1立方米的cube,即所有设定的几何体的包围盒大小是1立方米,并且确保轴心在原点,然后通过变换矩阵去变换这个几何体进行骨骼的可视化!数据提取通过把这个模型导出fbx然后丢到Unity读取所需要mesh类顶点数据
Unity端代码:
void Start()
{
MeshFilter meshRenderer = GetComponent<MeshFilter>();
var mesh = meshRenderer.sharedMesh;
var vs= mesh.vertices;
var ns= mesh.normals;
var _is = mesh.GetIndices(0);
print(_is.Length);
print(mesh.triangles.Length);
print(mesh.subMeshCount);
StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < mesh.vertexCount; i++)
{
sb.Append(vs[i].x.ToString("0.00") + ","+ vs[i].y.ToString("0.00") + ","+ vs[i].z.ToString("0.00") + ","+"\n");
}
print(mesh.vertexCount);
sb.Append("------------" + "\n");
for (int i = 0; i < mesh.vertexCount; i++)
{
sb.Append(ns[i].x.ToString("0.00") + "," + ns[i].y.ToString("0.00") + "," + ns[i].z.ToString("0.00") + "," + "\n");
}
sb.Append("------------" + "\n");
sb.Append(string.Join(",", mesh.triangles));
System.IO.File.WriteAllText("abc.txt",sb.ToString());
}
ps:这里的triangles就是我们的indices
float model_scale = 0.05f;
const float position[24 * 3] = {
//Pyramid
//0.00,-0.25,-0.50,
// 0.00,0.50,0.00,
// 0.50,-0.25,0.00,
// 0.50,-0.25,0.00,
// 0.00,0.50,0.00,
// 0.00,-0.25,0.50,
// 0.00,-0.25,0.50,
// 0.00,0.50,0.00,
// -0.50,-0.25,0.00,
// -0.50,-0.25,0.00,
// 0.00,0.50,0.00,
// 0.00,-0.25,-0.50,
// 0.50,-0.25,0.00,
// 0.00,-0.50,0.00,
// 0.00,-0.25,-0.50,
// 0.00,-0.25,0.50,
// 0.00,-0.50,0.00,
// 0.50,-0.25,0.00,
// -0.50,-0.25,0.00,
// 0.00,-0.50,0.00,
// 0.00,-0.25,0.50,
// 0.00,-0.25,-0.50,
// 0.00,-0.50,0.00,
// -0.50,-0.25,0.00
//Pyramid_B
/* 0.50,-0.50,-0.50,
0.00,0.50,0.00,
0.50,-0.50,0.50,
0.50,-0.50,0.50,
0.00,0.50,0.00,
-0.50,-0.50,0.50,
-0.50,-0.50,0.50,
0.00,0.50,0.00,
-0.50,-0.50,-0.50,
-0.50,-0.50,-0.50,
0.00,0.50,0.00,
0.50,-0.50,-0.50,
0.50,-0.50,-0.50,
0.50,-0.50,0.50,
-0.50,-0.50,0.50,
-0.50,-0.50,-0.50*/
//cube
/* 0.50,-0.50,0.50,
-0.50,-0.50,0.50,
0.50,0.50,0.50,
-0.50,0.50,0.50,
0.50,0.50,-0.50,
-0.50,0.50,-0.50,
0.50,-0.50,-0.50,
-0.50,-0.50,-0.50,
0.50,0.50,0.50,
-0.50,0.50,0.50,
0.50,0.50,-0.50,
-0.50,0.50,-0.50,
0.50,-0.50,-0.50,
0.50,-0.50,0.50,
-0.50,-0.50,0.50,
-0.50,-0.50,-0.50,
-0.50,-0.50,0.50,
-0.50,0.50,0.50,
-0.50,0.50,-0.50,
-0.50,-0.50,-0.50,
0.50,-0.50,-0.50,
0.50,0.50,-0.50,
0.50,0.50,0.50,
0.50,-0.50,0.50*/
//cube2
0.50,-0.50,0.50,
0.07,0.50,0.07,
-0.07,0.50,0.07,
-0.50,-0.50,0.50,
0.07,0.50,0.07,
0.07,0.50,-0.07,
-0.07,0.50,-0.07,
-0.07,0.50,0.07,
0.07,0.50,-0.07,
0.50,-0.50,-0.50,
-0.50,-0.50,-0.50,
-0.07,0.50,-0.07,
0.50,-0.50,-0.50,
0.50,-0.50,0.50,
-0.50,-0.50,0.50,
-0.50,-0.50,-0.50,
-0.50,-0.50,0.50,
-0.07,0.50,0.07,
-0.07,0.50,-0.07,
-0.50,-0.50,-0.50,
0.50,-0.50,-0.50,
0.07,0.50,-0.07,
0.07,0.50,0.07,
0.50,-0.50,0.50
};
float normal[24 * 3] = {
//Pyramid
/* 0.64,0.43,-0.64,
0.64,0.43,-0.64,
0.64,0.43,-0.64,
0.64,0.43,0.64,
0.64,0.43,0.64,
0.64,0.43,0.64,
-0.64,0.43,0.64,
-0.64,0.43,0.64,
-0.64,0.43,0.64,
-0.64,0.43,-0.64,
-0.64,0.43,-0.64,
-0.64,0.43,-0.64,
0.41,-0.82,-0.41,
0.41,-0.82,-0.41,
0.41,-0.82,-0.41,
0.41,-0.82,0.41,
0.41,-0.82,0.41,
0.41,-0.82,0.41,
-0.41,-0.82,0.41,
-0.41,-0.82,0.41,
-0.41,-0.82,0.41,
-0.41,-0.82,-0.41,
-0.41,-0.82,-0.41,
-0.41,-0.82,-0.41*/
//Pyramid_B
/* 0.89,0.45,0.00,
0.89,0.45,0.00,
0.89,0.45,0.00,
0.00,0.45,0.89,
0.00,0.45,0.89,
0.00,0.45,0.89,
-0.89,0.45,0.00,
-0.89,0.45,0.00,
-0.89,0.45,0.00,
0.00,0.45,-0.89,
0.00,0.45,-0.89,
0.00,0.45,-0.89,
0.00,-1.00,0.00,
0.00,-1.00,0.00,
0.00,-1.00,0.00,
0.00,-1.00,0.00*/
//cube
//0.00,0.00,1.00,
//0.00,0.00,1.00,
//0.00,0.00,1.00,
//0.00,0.00,1.00,
//0.00,1.00,0.00,
//0.00,1.00,0.00,
//0.00,0.00,-1.00,
//0.00,0.00,-1.00,
//0.00,1.00,0.00,
//0.00,1.00,0.00,
//0.00,0.00,-1.00,
//0.00,0.00,-1.00,
//0.00,-1.00,0.00,
//0.00,-1.00,0.00,
//0.00,-1.00,0.00,
//0.00,-1.00,0.00,
//-1.00,0.00,0.00,
//-1.00,0.00,0.00,
//-1.00,0.00,0.00,
//-1.00,0.00,0.00,
//1.00,0.00,0.00,
//1.00,0.00,0.00,
//1.00,0.00,0.00,
//1.00,0.00,0.00
//cube2
0.00,0.39,0.92,
0.00,0.39,0.92,
0.00,0.39,0.92,
0.00,0.39,0.92,
0.00,1.00,0.00,
0.00,1.00,0.00,
0.00,1.00,0.00,
0.00,1.00,0.00,
0.00,0.39,-0.92,
0.00,0.39,-0.92,
0.00,0.39,-0.92,
0.00,0.39,-0.92,
0.00,-1.00,0.00,
0.00,-1.00,0.00,
0.00,-1.00,0.00,
0.00,-1.00,0.00,
-0.92,0.39,0.00,
-0.92,0.39,0.00,
-0.92,0.39,0.00,
-0.92,0.39,0.00,
0.92,0.39,0.00,
0.92,0.39,0.00,
0.92,0.39,0.00,
0.92,0.39,0.00
};
vector<Vertex>vertices;
//vector<unsigned int> indices{ 0,2,3,0,3,1,8,4,5,8,5,9,10,6,7,10,7,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23 };//cube
vector<unsigned int> indices{ 0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23 };//cube2
//vector<unsigned int> indices{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 };//Pyramid
//vector<unsigned int> indices{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,12,14,15 };//Pyramid_B
//printf("indices:%d\n", indices.size());
vector<TextureInfo> textures;
for (int i = 0, max_len = 24 * 3; i < max_len; i += 3)
{
Vertex vertex;
vertex.Position = model_scale * glm::vec3(position[i], position[i + 1], position[i + 2]);//model_scale * glm::vec3(position[i], position[i + 1], position[i + 2]);//Scale
vertex.Normal = glm::vec3(normal[i], normal[i + 1], normal[i + 2]);
vertex.TexCoords = glm::vec2();
vertex.Tangent = glm::vec3();
vertex.Bitangent = glm::vec3();
vertex.m_BoneIDs[0] = 0;
vertex.m_BoneIDs[1] = 0;
vertex.m_BoneIDs[2] = 0;
vertex.m_BoneIDs[3] = 0;
vertex.m_Weights[0] = 0;
vertex.m_Weights[1] = 0;
vertex.m_Weights[2] = 0;
vertex.m_Weights[3] = 0;
vertices.push_back(vertex);
}
for (unsigned int i = 0; i < numJoints; ++i) {
if (pose.GetParent(i) < 0) {
continue;
}
//meshs.emplace_back(vertices, indices, textures, false);
p_mesh = new Mesh(vertices, indices, textures, false);
meshs.push_back(Mesh(vertices, indices, textures, false));
ps->use();
ps->setVec3("light_direction", -1.0f, -1.0f, -1.0f);
}
获取到关节父子节点世界坐标后,比如v1,v2,就可以用我们的变换矩阵操作了,那么核心代码是:
参数__length是关节长度,size是整体骨架大小,这里只能大致拟合,不能精确,想要相对精确需要借助模型包围盒尺寸推算__length和size
glm::mat4 ComputeBoneMatrix(const vec3& _start, const vec3& _end, float __length=14.5f, float size= 0.37f)
{
vec3 direction = (_end - _start);
auto _length = len(direction);//计算模长
auto _len_ = _length;
glm::mat4 model = glm::mat4(1.0f);
float height_adjust = 1.05f;
model = glm::translate(model, glm::vec3((_start.x + _end.x) * size, (_start.y + _end.y) * size + height_adjust, (_start.z + _end.z) * size));
float dir_x = direction.x, dir_y = direction.y, dir_z = direction.z;
if (0 == _length)
{
dir_x = 0.0; dir_y = 0.0; dir_z = 1.0; _length = 1.0;
}
dir_x /= _length;
dir_y /= _length;
dir_z /= _length;
double up_x, up_y, up_z;
up_x = 0.0;
up_y = 1.0;
up_z = 0.0;
double side_x, side_y, side_z;
side_x = up_y * dir_z - up_z * dir_y;
side_y = up_z * dir_x - up_x * dir_z;
side_z = up_x * dir_y - up_y * dir_x;
// x軸を単位ベクトルに正規化
_length = sqrt(side_x * side_x + side_y * side_y + side_z * side_z);
if (_length < 0.0001) {
side_x = 1.0; side_y = 0.0; side_z = 0.0; _length = 1.0;
}
side_x /= _length; side_y /= _length; side_z /= _length;
// z軸とx軸の外積からy軸の向きを計算
up_x = dir_y * side_z - dir_z * side_y;
up_y = dir_z * side_x - dir_x * side_z;
up_z = dir_x * side_y - dir_y * side_x;
glm::mat4 rot_matrx = {
side_x, side_y, side_z, 0.0,
up_x, up_y, up_z, 0.0,
dir_x, dir_y, dir_z, 0.0,
0.0, 0.0, 0.0, 1.0 };
auto result = glm::scale(glm::rotate(model * rot_matrx, glm::radians((float)(90)), glm::vec3(1, 0, 0)), glm::vec3(1, __length * _len_, 1));
return result;
}
另外这个法线扭曲是无解,最好重新计算,有大佬能解决就好了,当然也可以有骚操作,就是通过shader里把小于0.4的亮度的强行变为0.4
glsl:
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 light_direction;
void main()
{
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(-light_direction);
// diffuse shading
float diff = max(dot(norm, lightDir), 0.0);
if(diff<0.4)
diff=0.4;
vec3 color=vec3(0.447,1,0)*diff;
FragColor = vec4(color, 0.75);
}
这样可以实现最基本的光照,已经非常还原Unity的效果了