首页 > 其他分享 >03 加载模型

03 加载模型

时间:2023-05-12 22:46:46浏览次数:41  
标签:03 定义 模型 vertices 纹理 vector 数组 GL 加载

一、Assimp

  • Assimp可以将模型导成如下的格式:
  • 首先,该模型被打包成一个scene对象,是aiScene类型的,一般是导出为指向常量的指针。在scene中保存着三个对象:
    • 第一个是一个aiNode类型的指针,即根节点scene->mRootNode。
    • 第二个是一个aiMesh*类型的数组,即scene->mMeshes。保存着所有mesh的信息。
    • 第三个是一个aiMaterial*类型的数组,即scene->mMaterials。aiMaterial保存着一套纹理信息。
  • 其次,在每一个aiNode中,有两种对象
    • 第一个是mMeshes数组,里面保存着aiMesh中的索引;
    • 第二个是mChildren数组,里面保存着aiNode类型的指针,即其他子节点。
  • 此外,在每一个aiMesh中,保存着五种对象:
    • 第一个是mVerticles数组,里面保存着一系列的顶点位置,其数据类型是Assimp自定义的三维矢量,所以需要逐个元素的转移
    • 第二个是mNormals数组,里面保存着一系列的法向量,其位置与顶点是对应的,其数据类型是Assimp自定义的三维矢量,所以需要逐个元素的转移
    • 第三个是mTextureCoords数组,这是一个二维数组,第一个维度表示不同的纹理,第二个维度表示该纹理体系下每一个顶点的纹理坐标,其位置与顶点是对应的,其数据类型是Assimp自定义的二维矢量,所以需要逐个元素的转移;需要注意的是,specular与diffuse贴图是共用纹理坐标的,他们也是属于同一个material。
    • 第四个是mFaces数组,这是一个aiFace类的数组,aiFace类是一个图形类,代表三角形等基本的绘制图形,每一个aiFace对象face都有一个mIndices对象,这是一个对该aiMesh的顶点位置索引数组,表示这图形需要使用这些顶点来绘制,并且按照这个顺序来绘制。
    • 最后一个是mMaterialIndex,这是一个对scene->mMaterials的索引,一个mesh只有一个索引。scene->mMaterials的每一个元素是一个aiMaterial类的对象,aiMaterial类定义了一系列的函数,如GetTextureCount(aiTextureType type)可以返回diffuse或者specular纹理的数量,GetTexture(aiTextureType
      type, unsigned int i, aiString &str)可以返回该类型纹理中第i个纹理的地址,这个地址可能是相对路径也可能是绝对路径,取决于资源自身,基于这两个函数就可以按照之前的的流程定义纹理了。

二、网格

  • 回顾之前的内容可知,绘制在模型部分需要用到VAO,VBO,EBO,Texture,其中VBO需要一个顶点数组,包括顶点位置、法线、纹理坐标,EBO需要一个索引数组,最终表示成unsigned int的ID就可以使用,而纹理同样最终表示成ID,我们只要拿到id就可以使用了,至于纹理本身不需要记录的。我们将mesh作为一个基本的绘制单元,它包含自己的定点信息和纹理等信息,正如我们之前绘制的立方体一样。因此,我们期待将mesh作为一个类,并将绘制流程中所需要的操作和信息集成进去。
  • 除了shader相关的部分,都可以被集成:
  • 首先定义一个Vertex类,可以看出这个类包含着一个顶点的三种信息
struct Vertex {
	glm::vec3 Position;
	glm::vec3 Normal;
	glm::vec3 TexCoords;
};
  • 然后,在Mesh类中定义一个vector成员,是Vertex的容器,这样做的好处在于struct的成员是连续排布的,那么Vertex的vector就是一系列定点信息的连续排布,这是不是跟我们之前定义的float vertices[]有很好的对应了呢。但是,有一个差异在于,float[]的名字是指向数组首地址的指针,可以直接用到glBufferData中,但是vector的名字不是指针,所以需要索引处第一个元素并取址。这样,我们就定义好了第一个CPU数据vertices。
std::vector<Vertex> vertices;
  • 然后,在Mesh类中定义indices,这样我们就定义好了第二个CPU数据indices。
vector<unsigned int> indices;
  • 基于这两个数据,我们就可以生成VAO,VBO,EBO了,因此我们再定义这三个成员。
unsigned int VAO, VBO, EBO;
  • 此外,我们在定义一个纹理类Texture。其中除了最终的纹理id之外,我们还需要记录纹理类型(方便uniform操作),以及纹理对应图像的地址(避免重复定义纹理类对象,减少计算量)
struct Tecture {
	unsigned int id;
	string type;
	string path;
};
  • 并在Mesh中定义vector,来记录所有的纹理;注意,此处的纹理是最终结果,在Mesh中不进行纹理操作。
vector<Texture> textures;
  • 到目前为止,我们定义了纹理,定义了缓冲和其CPU数据,因为没有在这里定义纹理操作,所以还差缓冲操作,就可以完成前期的准备了;我们把它放在构造函数中。可以看到,除了三个数组的初始化之外,还在setupMesh函数中进行了缓冲操作,并定义了VAO VBO EBO。一个有趣的现象是vector的字节数采取了vector长度乘以元素size的方法,另外正如前面所述使用vector首元素的地址作为首地址,另外一个更为有趣的方法是使用offsetof来获得结构体中属性的偏置来作为顶点指针函数的偏置。
Mesh::Mesh(vector<Vertex> _vertices, vector<unsigned int> _indices, vector<Texture> _textures)
{
	vertices = _vertices;
	indices = _indices;
	textures = _textures;

	setupMesh();
}
void Mesh::setupMesh()
{
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);
	glGenVertexArrays(1, &VAO);

	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertices.size() * sizeof(Vertex), (void*)0);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertices.size() * sizeof(Vertex), (void*)(offsetof(Vertex,Normal)));
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertices.size() * sizeof(Vertex), (void*)(offsetof(Vertex, TexCoords)));
	glEnableVertexAttribArray(2);

	glBindVertexArray(0);
}
  • 到此为止,我们获得了绘制需要的东西VAO和纹理,前者表明缓冲操作完成了,但是后者还不完善,因为在绘制时需要将纹理传输进shader中,因此,我们再次定义一个绘制函数Draw,只需要一个Shader类参数就可以完成绘制了。当然,其他的shaderuniform操作不属于Mesh的任务,在Draw之外完成就可以了,但是这也是到目前的内容为止,仅剩的需要补充的内容了。
  • 在此之前,有一个问题需要解决,那就是我们已经在其他某个地方定义了纹理获得了id并激活了对应的位置,这个结果对应的id保存在了我们的vector中,但是我们还需要将纹理在shader中对应的sampler2D进行glUniformi的操作,但是如何保证uniform的结果即shadr中的某个纹理(在程序中发挥着某种着色作用)被正确的绑定到了某个id上呢。按照之前的shader,我们对纹理的区分只有diffuse和specular,那么在这个基础上,我们只要能够区分这两个就可以达到效果了。这也正是为什么在Texture类中加入了type的原因。
void Mesh::Draw(Shader shader) const
{
	shader.use();

	unsigned int diffuseNr = 1;
	unsigned int specularNr = 1;

	for (int i = 0; i != textures.size(); i++)
	{
		string number;
		string name = textures[i].type;
		if (name == "diffuse_texture")
		{
			number = std::to_string(diffuseNr++);
		}
		else if (name == "specular_texture")
		{
			number = std::to_string(specularNr++);
		}
		shader.setInt(("material."+name+number).c_str(), i);
	}

	glBindVertexArray(VAO);
	glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
}

标签:03,定义,模型,vertices,纹理,vector,数组,GL,加载
From: https://www.cnblogs.com/etherovo/p/17394243.html

相关文章

  • 【五期邹昱夫】CCF-A(NeurIPS'21)Gradient inversion with generative image prior
    "JeonJ,LeeK,OhS,etal.Gradientinversionwithgenerativeimageprior[J].Advancesinneuralinformationprocessingsystems,2021,34:29898-29908."  本文提出了一种基于预训练模型的梯度反演方法。该方法通过使用潜在空间搜索优化维度较低的特征向量,减少......
  • 钢管订购和运输模型——Python实现
    要铺设一条\(A_1→A_2→…→A_{15}\)的输送天然气的主管道,如图所示。经筛选后可以生产这种主管道钢管的钢厂有\(S_1,S_2,…,S_7\)。图中粗线表示铁路,单细线表示公路,双细线表示要铺设的管道(假设沿管道或者原有公路,或者建有施工公路),圆圈表示火车站,每段铁路、公路和管道旁的......
  • 剑指 Offer 03. 数组中重复的数字
    剑指Offer03.数组中重复的数字题目描述找出数组中重复的数字。在一个长度为n的数组nums里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。2=n<=100000解法1.先进行......
  • 1103.模版变量及模版过滤器
    一、模版路径总结在配置文件setting.py文件中找到TEMPLATES进行文件路径配置:1.DIRS定义一个目录列表,模板引擎按流标顺序搜索这些目录以查询模板源文件。将templates放在主项目目录下:2.APP_DIRS告诉模板引擎是否应该进入每个已安装的应用中查找模板,值为True则模板会去安装了......
  • 代码随想录算法训练营第三天|203.移除链表元素 、707.设计链表 、206.反转链表
    一.链表基础1.最后一个节点的指针域指向null(空指针的意思)。2.链表在内存中不是连续分布的。3.链表的长度可以是不固定的,并且可以动态增删,适合数据量不固定,频繁增删,较少查询的场景。1#链表节点的定义2classListNode:3def__init__(self,val,next=None):4......
  • 什么是人工智能领域模型的 temperature 参数?
    在人工智能领域中,温度参数(temperatureparameter)是指在生成式模型中使用的一种技术,可以用于控制生成结果的多样性和随机性。温度参数通常用于一种叫做“softmax”概率分布的算法中,该算法被广泛应用于生成式模型中,包括机器翻译、自然语言处理和图像生成等领域。在softmax算法中,温......
  • 什么是人工智能领域模型的 Presence Penalty 参数?
    在人工智能领域中,模型的质量往往受到许多因素的影响,其中一个重要的因素是模型的PresencePenalty参数。PresencePenalty可以被理解为一种正则化项,它被添加到模型的损失函数中,以惩罚模型对一些特定的特征或信息进行过多地关注。在人工智能领域,尤其是自然语言处理(NLP)领域,生成模......
  • 使用键盘控制gazebo小车模型运动
    博客地址:https://www.cnblogs.com/zylyehuo/gazebo小车模型创建详见另一篇博客博客地址:gazebo小车模型(附带仿真环境)-zylyehuo-博客园参考链接Autolabor-ROS机器人入门课程《ROS理论与实践》ROS源码安装teleop_twist_keyboard成果图step1:打开vscode进入之前创......
  • fatal: detected dubious ownership in repository at 'D:/xxx'
     git的出现这个错误: 执行下面代码即可:gitconfig--global--addsafe.directory"*"; ......
  • Picturefill.WP – 根据屏幕尺寸加载合适的图片
    Picturefill.WP插件利用picturefill.js脚本展示Responsive图片,即根据视口宽度选择尺寸合适的图片加载,节省带宽,提高网站载入速度。例如用户用手机访问站点,该插件会选择适合手机尺寸的图片(如缩略图)加载,不会加载完整尺寸图片。使用方法没有选项,无需任何设置,下载插件激活,网站文章中的所......