2 - 进阶操作(可能)

接下来我们试着给场景加点光源,然后导入其他模型,完善一下代码。

增添代码细节

丰富顶点信息

除了位置,法线和纹理坐标,顶点还有切线(Tangent),副切线(Bitangent)等信息:

#define MAX_BONE_INFLUENCE 4
struct Vertex
{
	glm::vec3 Position;
	glm::vec3 Normal;
	glm::vec2 TexCoords;
	glm::vec3 Tangent;
	glm::vec3 Bitangent;
	// bone indexes
	int m_BoneIDs[MAX_BONE_INFLUENCE];
	// weight from each bone
	float m_Weights[MAX_BONE_INFLUENCE];
};

记得修改一下顶点属性:

// 顶点位置属性
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glCheckError();
// 顶点法线属性
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
glCheckError();
// 顶点纹理坐标属性
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
glCheckError();
// 顶点切线属性
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));
glCheckError();
// 顶点副切线属性
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));
glCheckError();
// 顶点骨骼属性-ids
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, MAX_BONE_INFLUENCE, GL_INT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, m_BoneIDs));
glCheckError();
// 顶点骨骼属性-weight
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, MAX_BONE_INFLUENCE, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, m_Weights));
glCheckError();

这里为了方便debug,使用了这里提供的glCheckError()宏定义,方便定位是哪个文件哪一行有问题。

然后改一下读取顶点属性的代码:

// 处理顶点信息(位置,法线,纹理坐标)
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
	Vertex vertex;
	// 位置
	glm::vec3 vector;
	vector.x = mesh->mVertices[i].x;
	vector.y = mesh->mVertices[i].y;
	vector.z = mesh->mVertices[i].z;
	vertex.Position = vector;
	
	// 法线
	if (mesh->HasNormals())
	{
		vector.x = mesh->mNormals[i].x;
		vector.y = mesh->mNormals[i].y;
		vector.z = mesh->mNormals[i].z;
		vertex.Normal = vector;
	}
	else
	{
		vertex.Normal = glm::vec3(0.0f, 0.0f, 0.0f);
	}
	
	// 纹理坐标
	if (mesh->mTextureCoords[0])
	{
		glm::vec2 vec;
		vec.x = mesh->mTextureCoords[0][i].x;
		vec.y = mesh->mTextureCoords[0][i].y;
		vertex.TexCoords = vec;
	}
	else
	{
		vertex.TexCoords = glm::vec2(0.0f, 0.0f);
	}

	// 切线
	if (mesh->HasTangentsAndBitangents())
	{
		vector.x = mesh->mTangents[i].x;
		vector.y = mesh->mTangents[i].y;
		vector.z = mesh->mTangents[i].z;
		vertex.Tangent = vector;

		vector.x = mesh->mBitangents[i].x;
		vector.y = mesh->mBitangents[i].y;
		vector.z = mesh->mBitangents[i].z;
		vertex.Bitangent = vector;
	}
	else
	{
		vertex.Bitangent = glm::vec3(0.0f, 0.0f, 0.0f);
		vertex.Tangent = glm::vec3(0.0f, 0.0f, 0.0f);
	}
	vertices.push_back(vertex);
}

有关法线和切线的计算,可以通过aiProcess_GenSmoothNormalsaiProcess_CalcTangentSpace这两个后处理参数来获得。

丰富材质信息

除了高光贴图和漫反射贴图外,还有法线贴图,高度贴图等。在读取贴图时,把它们加上去:

std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());

std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());

然后将贴图信息写入着色器:

// ...
else if (name == "texture_normal")
{
	number = std::to_string(normalNr++);
}
else if (name == "texture_height")
{
	number = std::to_string(heightNr++);
}

加点光源

可以加两个点光源,看看模型效果怎么样

载入其他模型

教程的背包

参考资料

  • 模型 - LearnOpenGL CN (learnopengl-cn.github.io)
  • 调试 - LearnOpenGL CN (learnopengl-cn.github.io)