7.5使用材质


文档摘要

## 7.5 使用材质 项目结构有所变化,渲染相关代码都整理到 目录,如下图。 本书教程里的材质文件,是以 格式组织的,这里引入 这个xml库,对材质文件进行解析。 解析材质文件 按照材质文件的xml节点父子关系,创建类 ,并将xml节点数据传入对应类的 函数进行解析。 Material 在类 中,传入 材质文件路径,使用 解析,遍历节点并存储。 对XML进行解析时,创建了Shader,生成了Texture,保存为成员变量,方便渲染时获取并使用。 使用材质 OpenGL渲染一个物体,只需要准备顶点、Texture,然后运行对应的Shader,设置好状态。 顶点由 提供,Texture和Shader都在材质的 节点创建好了,万事俱备,按照顺序进行渲染就好了。

7.5 使用材质

CLion项目文件位于 samples\mesh_and_material\use_material

项目结构有所变化,渲染相关代码都整理到source/renderer目录,如下图。

本书教程里的材质文件,是以xml格式组织的,这里引入rapidxml这个xml库,对材质文件进行解析。

1. 解析材质文件

按照材质文件的xml节点父子关系,创建类 Material,并将xml节点数据传入对应类的Parse函数进行解析。

Material

<material shader="shader/unlit"> <texture name="u_diffuse_texture" image="images/urban.cpt"/> </material>

在类Material中,传入.mat材质文件路径,使用rapidxml解析,遍历节点并存储。

void Material::Parse(string material_path) { //解析xml rapidxml::file<> xml_file((Application::data_path()+material_path).c_str()); rapidxml::xml_document<> document; document.parse<0>(xml_file.data()); //根节点 rapidxml::xml_node<>* material_node=document.first_node("material"); if(material_node == nullptr){ return; } rapidxml::xml_attribute<>* material_shader_attribute=material_node->first_attribute("shader"); if(material_shader_attribute == nullptr){ return; } shader_=Shader::Find(material_shader_attribute->value()); //解析Texture rapidxml::xml_node<>* material_texture_node=material_node->first_node("texture"); while (material_texture_node != nullptr){ rapidxml::xml_attribute<>* texture_name_attribute=material_texture_node->first_attribute("name"); if(texture_name_attribute == nullptr){ return; } rapidxml::xml_attribute<>* texture_image_attribute=material_texture_node->first_attribute("image"); if(texture_image_attribute == nullptr){ return; } std::string shader_property_name=texture_name_attribute->value(); std::string image_path=texture_image_attribute->value(); textures_.push_back(std::make_pair(texture_name_attribute->value(), Texture2D::LoadFromFile(image_path))); material_texture_node=material_texture_node->next_sibling("texture"); } }

对XML进行解析时,创建了Shader,生成了Texture,保存为成员变量,方便渲染时获取并使用。

2. 使用材质

OpenGL渲染一个物体,只需要准备顶点、Texture,然后运行对应的Shader,设置好状态。
顶点由MeshFilter提供,Texture和Shader都在材质的pass节点创建好了,万事俱备,按照顺序进行渲染就好了。

  1. 初始化OpenGL环境
  2. 创建MeshFilter
  3. 创建Material
  4. Material获取Shadergl_program_id,指定为目标Shader程序。
  5. 上传顶点数据,生成缓冲区对象,指定纹理单元。
  6. 调用glDrawElements提交渲染命令及数据。

修改main.cppmain() 函数:

2.1 创建Material对象解析材质

Material* material=new Material(); material->Parse("material/cube.mat");

2.2 从Pass获取数据进行渲染

//file:source/main.cpp line:101 int main(void) { ...... Material* material=new Material(); material->Parse("material/cube.mat"); //获取`Shader`的`gl_program_id`,指定为目标Shader程序。 GLuint gl_program_id=material->shader()->gl_program_id(); mvp_location = glGetUniformLocation(gl_program_id, "u_mvp"); vpos_location = glGetAttribLocation(gl_program_id, "a_pos"); vcol_location = glGetAttribLocation(gl_program_id, "a_color"); a_uv_location = glGetAttribLocation(gl_program_id, "a_uv"); GeneratorVertexArrayObject(); GeneratorBufferObject(); while (!glfwWindowShouldClose(window)) { ...... //指定GPU程序(就是指定顶点着色器、片段着色器) glUseProgram(gl_program_id); { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE);//开启背面剔除 //上传mvp矩阵 glUniformMatrix4fv(mvp_location, 1, GL_FALSE, &mvp[0][0]); //拿到保存的Texture std::vector<std::pair<std::string,Texture2D*>> textures=material->textures(); for (int texture_index = 0; texture_index < textures.size(); ++texture_index) { GLint u_texture_location= glGetUniformLocation(gl_program_id, textures[texture_index].first.c_str()); //激活纹理单元0 glActiveTexture(GL_TEXTURE0+texture_index); //将加载的图片纹理句柄,绑定到纹理单元0的Texture2D上。 glBindTexture(GL_TEXTURE_2D,textures[texture_index].second->gl_texture_id()); //设置Shader程序从纹理单元0读取颜色数据 glUniform1i(u_texture_location,texture_index); } glBindVertexArray(kVAO); { glDrawElements(GL_TRIANGLES,mesh_filter->mesh()->vertex_index_num_,GL_UNSIGNED_SHORT,0);//使用顶点索引进行绘制,最后的0表示数据偏移量。 } glBindVertexArray(0); } ...... } ...... }

3. 测试

编译运行项目,正确渲染了立方体:


发布者: 作者: 转发
评论区 (0)
U