openGL加载obj三维模型 您所在的位置:网站首页 glad是怎么读 openGL加载obj三维模型

openGL加载obj三维模型

2024-01-22 00:51| 来源: 网络整理| 查看: 265

openGL系列文章目录

文章目录 openGL系列文章目录前言一、obj格式三维模型二、obj文件格式2.读入数据obj文件解析obj渲染 运行效果源码下载

前言

复杂的3D 模型,例如在视频游戏或计算机生成的电影中的人物角色,通常使用建模工具生成。这种“DCC”(数字内容创建)工具使人们(例如艺术家)能够在3D 空间中构建任意形状并自动生成顶点、纹理坐标、顶点法向量等。有太多这样的工具,此处无法一一列出,有几个例子是3dsMax、MAYA、Blender、Lightwave、Cinema4D 等。3dsMax 屏幕示例。 在这里插入图片描述 在这里插入图片描述

一、obj格式三维模型

OBJ 文件很简单,我们可以相对容易地开发一个基本的导入器。在OBJ 文件中,通过文本行的形式指定顶点几何数据、纹理坐标、法向量和其他信息。它有一些限制——例如,OBJ 文件无法指定模型动画。OBJ 文件中的行,以字符标记开头,表示该行上的数据类型。一些常见的标签包括: 􀀠 v-几何(顶点位置)数据; 􀀠 vt-纹理坐标; 􀀠 vn-顶点法向量; 􀀠 f-面(通常是三角形中的顶点)。 还有其他标签可以用来存储对象名称、使用的材质、曲线、阴影和许多其他细节

二、obj文件格式

在这里插入图片描述

2.读入数据

在这里插入图片描述 红色的值(以“vt”开头)是各种纹理坐标。纹理坐标列表比顶点列表长的原因是一些顶点参与多个三角形,并且在这些情况下可能使用不同的纹理坐标。绿色的值(以“vn”开头)是各种法向量。该列表通常也比顶点列表长(尽管在该示例中不是这样),同样是因为一些顶点参与多个三角形,并且在那些情况下可能使用不同的法向量。 在文件底部附近标记为紫色的值(以“f”开头)指定三角形(即“面”)。在此示例中,每个面(三角形)具有3 个元素,每个元素具有由“/”分隔的3 个值(OBJ 也允许其他格式)。每个元素的值分别是顶点列表、纹理坐标和法向量的索引。例如,第三个面是: f 2 / 7 / 3 5 / 8 / 3 3 / 9 / 3 这表明顶点列表中的第2 个、第5 个和第3 个顶点(蓝色)组成了一个三角形(请注意 OBJ 索引从1 开始)。相应的纹理坐标是红色部分中纹理坐标列表中的第7 项、第8 项和第9 项。所有3 个顶点都具有相同的法向量,也就是以绿色显示的法向量列表中的第3 项。 OBJ 格式的模型并不要求具有法向量,甚至纹理坐标。如果模型没有纹理坐标或法向量, 则面的数值将仅指定顶点索引: f 2 5 3 如果模型具有纹理坐标,但不具有法向量,则格式如下: f 2 / 7 5 / 8 3 / 9 并且,如果模型具有法向量但没有纹理坐标,则格式为: f 2 / / 3 5 / / 3 3 / / 3 模型具有数万个顶点并不罕见。对于所有可以想象的应用场景,几乎都可以在互联网上 下载到数百种这样的模型,包括动物、建筑物、汽车、飞机、神话生物、人等。 在互联网上可以获得可以导入OBJ 模型的复杂程序各不相同的导入程序。编写一个非 常简单的OBJ 加载器函数也并不困难,它可以处理我们看到的基本标记(v、vt、vn 和f)。

obj文件解析

头文件

#pragma once #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/type_ptr.hpp" #include #include #include #include #include using namespace std; class ImportedModel { private: int _numVertices; //所有顶点坐标总数 std::vector _vertices; //所有顶点个数,一个顶点个数包含(x,y,z) std::vector _texCoords; //纹理坐标(u,v) std::vector _normalVecs; //法线 public: ImportedModel(); ImportedModel(const char* filePath); int getNumVertices(); std::vector getVertices(); std::vector getTextureCoords(); std::vector getNormals(); }; class ModelImporter { private: std::vector _vertVals; std::vector _triangleVerts; std::vector _textureCoords; std::vector _stVals; std::vector _normals; std::vector _normVals; public: ModelImporter(); void parseOBJ(const char* filePath); int getNumVertices(); std::vector getVertices(); std::vector getTextureCoordinates(); std::vector getNormals(); };

实现文件

#include "ImportedModel.h" #include ImportedModel::ImportedModel() { } ImportedModel::ImportedModel(const char* filePath) { ModelImporter modelImporter = ModelImporter(); modelImporter.parseOBJ(filePath); _numVertices = modelImporter.getNumVertices(); vector verts = modelImporter.getVertices(); vector tcs = modelImporter.getTextureCoordinates(); vector normals = modelImporter.getNormals(); for (int i=0; i return _numVertices; } std::vector ImportedModel::getVertices() { return _vertices; } std::vector ImportedModel::getTextureCoords() { return _texCoords; } std::vector ImportedModel::getNormals() { return _normalVecs; } /// /// ModelImporter implement /// ModelImporter::ModelImporter() { } void ModelImporter::parseOBJ(const char* filePath) { float x = 0.f, y = 0.f, z = 0.f; string content; ifstream fileStream(filePath, ios::in); string line = ""; while (!fileStream.eof()) { getline(fileStream, line); if (line.compare(0, 2, "v ") == 0) //注意v后面有空格 { std::stringstream ss(line.erase(0, 1)); ss >> x >> y >> z; //ss >> x; ss >> y; ss >> z; _vertVals.push_back(x); _vertVals.push_back(y); _vertVals.push_back(z); } if (line.compare(0, 2, "vt") == 0) { std::stringstream ss(line.erase(0, 2)); ss >> x >> y; _stVals.push_back(x); _stVals.push_back(y); } if (line.compare(0, 2, "vn") == 0) { std::stringstream ss(line.erase(0, 2)); ss >> x >> y >> z; _normVals.push_back(x); _normVals.push_back(y); _normVals.push_back(z); } if (line.compare(0, 1, "f") == 0) //原书有误 { string oneCorner, v, t, n; std::stringstream ss(line.erase(0, 2)); for (int i = 0; i return (_triangleVerts.size() / 3); } std::vector ModelImporter::getVertices() { return _triangleVerts; } std::vector ModelImporter::getTextureCoordinates() { return _textureCoords; } std::vector ModelImporter::getNormals() { return _normals; } obj渲染 #include "glew/glew.h" #include "glfw/glfw3.h" #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/type_ptr.hpp" #include "Utils.h" #include "ImportedModel.h" #include "camera.h" #include #include #include #include using namespace std; static const int screen_width = 1920; static const int screen_height = 1080; static const int numVAOs = 1; static const int numVBOs = 3; static const float pai = 3.1415926f; GLuint renderingProgram = 0; GLuint vao[numVAOs] = { 0 }; GLuint vbo[numVBOs] = { 0 }; int width = 0; int height = 0; float aspect = 0.f; float objLocX = 0.f, objLocY = 0.f, objLocZ = 0.f; GLuint mvLoc = 0; GLuint projLoc = 0; GLuint shuttleTextureId = 0; glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f); Camera camera(glm::vec3(0.f, 0.f, 2.f)); float cameraX, cameraY, cameraZ; ImportedModel myModel("resources/shuttle.obj"); GLboolean keys[1024] = { GL_FALSE }; GLboolean firstMouse = GL_TRUE; float deltaTime = 0.f; float lastFrame = 0.f; float lastLocX = 0.f; float lastLocY = 0.f; float toRadians(float degrees) { return (degrees * 2.f * pai) / 360.f; } void setupVertices(void) { vector vert = myModel.getVertices(); vector text = myModel.getTextureCoords(); vector norm = myModel.getNormals(); vector pValues; vector tValues; vector nValues; for (int i=0; i renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl"); //glfwGetWindowSize(window, &width, &height); cameraX = 0.0f; cameraY = 0.0f; cameraZ = 2.f; objLocX = 0.0f; objLocY = 0.0f; objLocZ = 0.0f; glfwGetFramebufferSize(window, &width, &height); aspect = (float)width / (float)height; pMat = glm::perspective(toRadians(45.f), aspect, 0.01f, 1000.f); setupVertices(); shuttleTextureId = Utils::loadTexture("resources/spstob_1.jpg"); /*lastLocX = (float)screen_width / 2.f; lastLocY = (float)screen_height / 2.f;*/ } void window_size_callback(GLFWwindow* window, int newWidth, int newHeight) { //屏幕坐标和窗口的帧缓冲 /*GLFW在这里和这里解释文档中的两个坐标系。 简而言之,窗口坐标是相对于监视器和 / 或窗口的,并且以不一定对应于真实屏幕像素的人造单元给出。 当DPI缩放被激活时(例如,在带有视网膜显示器的Mac上),情况尤其如此。 与窗口坐标相比,帧缓冲区的大小与像素相关,以便与glViewport OpenGLs要求相匹配。 请注意,在某些系统上,窗口坐标和像素坐标可以相同,但这不一定是正确的。*/ aspect = (float)newWidth / (float)newHeight; glViewport(0, 0, newWidth, newHeight); cameraX = 0.0f; cameraY = 0.0f; cameraZ =4.f; objLocX = 0.0f; objLocY = 0.0f; objLocZ = 0.0f; mMat = glm::translate(glm::mat4(1.f), glm::vec3(objLocX, objLocY, objLocZ)); //pMat = glm::perspective(glm::radians(45.f), aspect, 0.001f, 1000.f); pMat = glm::perspective(camera.Zoom, aspect, 0.001f, 1000.f); } void do_movement(void) { if (keys[GLFW_KEY_W]) { camera.ProcessKeyboard(FORWARD, deltaTime); } if (keys[GLFW_KEY_S]) { camera.ProcessKeyboard(BACKWARD, deltaTime); } if (keys[GLFW_KEY_A]) { camera.ProcessKeyboard(LEFT, deltaTime); } if (keys[GLFW_KEY_D]) { camera.ProcessKeyboard(RIGHT, deltaTime); } } void display(GLFWwindow* window, double currentTime) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(0.1f, 0.5f, 0.5f, 1.f); //启动着色器程序,在GPU上安装GLSL代码,这不会运行着色器程序, glUseProgram(renderingProgram); deltaTime = currentTime - lastFrame; lastFrame = currentTime; do_movement(); //获取uniform变量在着色器程序中的位置序号,通过该序号可以设置一致变量的值,如果没有该变量则返回-1 mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix"); projLoc = glGetUniformLocation(renderingProgram, "proj_matrix"); mMat = glm::translate(glm::mat4(1.f), glm::vec3(objLocX, objLocY, objLocZ)); //vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ)); vMat = camera.GetViewMatrix(); mMat = glm::rotate(mMat, toRadians(0.f), glm::vec3(1.f, 0.f, 0.f)); mMat = glm::rotate(mMat, toRadians(35.f), glm::vec3(0.f, 1.f, 0.f)); mMat = glm::rotate(mMat, toRadians(35.f), glm::vec3(0.f, 0.f, 1.f)); //这句必须要有,否则鼠标中建失效 pMat = glm::perspective(camera.Zoom, aspect, 0.01f, 1000.f); mvMat = vMat * mMat; //更改一个uniform矩阵变量或数组的值。要更改的uniform变量的位置由location指定,location的值应该由glGetUniformLocation函数返回 // 将透视矩阵和MV 矩阵复制给相应的统一变量 /*通过一致变量(uniform修饰的变量)引用将一致变量值传入渲染管线。 location : uniform的位置。 count : 需要加载数据的数组元素的数量或者需要修改的矩阵的数量。 transpose : 指明矩阵是列优先(column major)矩阵(GL_FALSE)还是行优先(row major)矩阵(GL_TRUE)。 value : 指向由count个元素的数组的指针。 */ glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat)); glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat)); //GL_FALSE 参数不能错否则无法显示obj模型 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shuttleTextureId); glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); //指定了渲染时索引值为 index 的顶点属性数组的数据格式和位置 /*Parameters index 指定要修改的顶点属性的索引值 size 指定每个顶点属性的组件数量。必须为1、2、3或者4。初始值为4。(梦维:如position是由3个(x, y, z)组成,而颜色是4个(r, g, b, a)) type 指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。 normalized 指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)。 stride 指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0。 pointer 指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0。 */ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); //如果启用,将访问通用顶点属性数组中的值,并在调用顶点数组命令(如glDrawArrays或glDrawElements)时用于呈现。 //对应顶点着色器的layout(location=0) in vec3 position; glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, vbo[2]); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(2); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shuttleTextureId); glEnable(GL_DEPTH_TEST); //指定用于深度缓冲比较值; glDepthFunc(GL_LEQUAL); glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices()); } void press_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) { if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS)) { glfwSetWindowShouldClose(window, GLFW_TRUE); } if (action == GLFW_PRESS) { keys[key] = GLFW_TRUE; } else if (action == GLFW_RELEASE) { keys[key] = GLFW_FALSE; } } void mouse_move_callback(GLFWwindow* window, double xPos, double yPos) { if (firstMouse) { lastLocX = xPos; lastLocY = yPos; firstMouse = GL_FALSE; } double offsetLocX = xPos - lastLocX; double offsetLocY = lastLocY - yPos; lastLocX = xPos; lastLocY = yPos; camera.ProcessMouseMovement(offsetLocX, offsetLocY); } void mouse_scroll_callback(GLFWwindow* window, double xPos, double yPos) { camera.ProcessMouseScroll(yPos); } int main(int argc, char** argv) { int glfwState = glfwInit(); if (GLFW_FALSE == glfwState) { cout cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有