C++/OpenGL 入门(18):读取obj文件并贴图

article/2025/10/25 3:51:57
  1. 来源:《Computer Graphics Programming in OpenGL Using C++ 》by V Scott
    Gordon John L Clevenger
  2. 内容:程序6.3 Simple (Limited) OBJ Loader 简单的obj文件读取器,书P152页,PDF171/403

结果

生成:

在这里插入图片描述

读取:

在这里插入图片描述
在这里插入图片描述

笔记

obj文件的格式介绍

  • 加载外部绘图的模型:复杂的3D模型一般是由建模软件产生的。3D模型的格式 .obj, .3ds, .ply, .mesh, 其中最简单是的obj文件。
  • Obj文件比较简单,我们程序读取的时候也较为容易。Obj文件中,确定了点几何信息,纹理坐标,法向量和其他信息,当然也有一些限制,比如obj文件无法确定模型的运动。
    Obj文件中,开头是一系列的特征标签,提示存储的是什么样的数据。一些常用的标签有:
    V——顶点几何信息
    Vt——纹理坐标
    Vn——顶点法向量
    F——face 面(三角形的三个顶点)
  • 其他的标签可能存储的是对象的名字,所用的材料,曲率curves, 阴影 shadows和其他的细节。
    我们将讨论限制在以上四个标签,这足够展示一系列复杂模型。
    假设用Blender软件建立一个四面体,然后选择.obj格式导出文件包括纹理坐标和顶点法向量
  • 在obj文件用txt文件打开,头几行**“#“**是注释,编译器忽略注释的这几行。 “o“ 表示obj文件的名字,编译器也需要忽略
    s“开头的语句,说明这些面不该被平滑,编译器也会忽略该行
    v“开头的语句,表示四面体的5个点的相对于原点(0,0,0)的X,Y,Z坐标,原点在四面体的中心 以下是书中的例子给的obj文件
    在这里插入图片描述
  • vt“开头的红色行代表的是不同的纹理坐标,纹理坐标的数量比顶点数量多,是因为一个顶点可能参与多个三角形,在这些不同三角形中有不同的纹理坐标。
  • vn“绿色行,表示不同的法向量,法向量的行数通常比顶点的行数多,是因为有一些顶点同时在不同三角形中。
  • f“紫色行,表示三角形,每个面有三个元素,是用/分隔,比如下图:
  • 暗示说,第2.5.3的顶点组成一个三角形
    每一组的第二个数 : 7 8 9 是 vt中的,也就是纹理坐标组成的三角形
    每一组的第三个数 3 3 3 是vn 中的,也就是第三行,这三个点组成的面有一样的法向量
  • 如果obj文件不包括纹理坐标和法向量,格式为: f 2 5 3
    如果obj文件有纹理坐标,没有法向量,格式为: f 2/7 5/8 3/9
    如果obj文件没有纹理坐标,有法向量,格式为: f 2//3 5//3 3//3
  • 可以根据标签 v,vt,vn,f自己写一个obj文件

Obj文件的限制
① 只支持全部表面都是三角形的模型,也就是说顶点位置,纹理坐标和法向量必须全部说明,并且以这样的形式: f #/#/# #/#/# #/#/#
② 材料的标签被忽略后,纹理贴图的做法必须用第五章的方法完成
③ 只有全部是三角形网格组成的obj模型才能支持,其余复杂网格模型的obj文件不支持
④ 每一行的元素由空格分隔开

  • 索引indexing对obj有另外的限制:
    obj文件中两个不同的“face”行可能包含指向相同顶点但不同纹理坐标的索引。但是,OpenGL的索引机制只能指向一个特定的顶点及其属性。使用OpenGL索引将需要确保一个面的vvtvn值的整个组合都保存在各自的数组中。
  • 一种更简单,但效率较低的替代方法是为每个三角形面入口创建一个新顶点。尽管使用OpenGL索引具有节省空间的优势,但为了清晰起见,我们选择了这种更简单的方法。
  • ModelImporter 类中有一个函数parseOBJ(),能够逐行读取obj文件,分别处理四种情况 v, vt, vn, and f。在每一种情况中,每一行的数据都会被读取。首先用 erase() 函数跳过 v, vt, vn, and f的开头,然后用stringstream 类中的“>>” 符号提取接下来的每一个值,最后储存在float的向量vector中。当处理面 f 的时候,顶点是用之前创建的顶点位置,纹理坐标和法向量处理的。
  • ModelImporter 类是存储在ImportedModel class的类中,是通过将点存储在2维 vec2和3维vec3向量中来加载和读取obj文件的点信息。ModelImporter 类和ImportedModel类都在GLM类当中,用来存储顶点信息,纹理坐标和法向量。ImportedModel类中的处理器能够使得C++/OpenGL能够读取模型,就像第五章建立sphere和圆环torus的类一样。
  • 接下来将要使用 ModelImporter and ImportedModel类进行加载obj文件,将其中的顶点信息转入一系列的VBO中为后续渲染。

安装 Blender

  • Blender 软件下载网址:Blender 下载网址,
    选择下图的 Aliyun,即可下载。

在这里插入图片描述
软件界面中有新手教程自带正方体,右上角“文件”->“导出”->“obj”
在这里插入图片描述
在这里插入图片描述
在obj导出弹窗,选择存储位置和存储名称,选择包括在内的内容,包括顶点,法线和三角面
在这里插入图片描述
然后在相应位置会生成obj文件,可以右键选择用记事本打开
在这里插入图片描述

调试中的错误与解决办法

  • 当把 obj 文件包含在项目中,运行的时候会报错:LNK1107 文件无效或损坏:无法在0x3E2处读取
    解决方法是:将obj文件右键,选择“从项目中排除”,这样运行就不报错了,但是运行时无法出现四面体的窗口,只出现了终端
    在这里插入图片描述
    在这里插入图片描述
    将obj排除在项目外后,能够成功生成exe,但是并无弹窗
    在这里插入图片描述
    将6-3 cube1.obj排除在项目外后,出现错误:
    Debug Assertopm Failed!
    Expression:vector subscript out of range

    在这里插入图片描述

查找错误得方法:通过用 print(“成功运行\n xxx语句\n”)放在要检测的函数后面,运行,看弹出的终端窗口是否出现这句话,如果出现,说明被检测的函数可以正常运行,如果没有出现,就说明被检测函数有误,需要跳到函数定义中,逐句再查看。
通过逐一观察,发现:numObjVertices 这个值没有正常获取数值,还是0的状态,导致后面的遍历无法进行。同时,i < numObjVertices-1 应该改为 i < numObjVertices

  • 关于stringstream 的应用参考: c++ stringstream ss()_wesigj的博客-CSDN博客_istringstream ss(str)
    在这里插入图片描述
    找到错误原因了:
    Line.compare(0, 2, “f ”); 表示,从索引0开始的2个字符进行对比,所以f 后面一定要加一个空格,构成2个字符,
    在这里插入图片描述

完整代码

一共需要 个文件,分别是:
① 220302 6.3 OBJLoader.cpp 主程序
② 6.3 ImportedModel.cpp 一个类,用于读取obj文件
③ 6.3 ImportedModel.h 类的声明
④ 5.2 Utils.cpp 一些调用函数,比如检测错误的函数
⑤ 5.2 Utils.h 调用函数声明
⑥ 5.2 vertShader.glsl 点着色器,确定3D点坐标
⑦ 5.2 fragShader.glsl 片着色器,确定每个点的颜色
⑧ 6.3 cube1.obj obj文件,这里用的是Blender软件生成的
⑨ 6.1 earth.jpg 一张图片

① 220302 6.3 OBJLoader.cpp 主程序

#include <string>
#include <iostream>
#include <fstream>
#include <cmath>#include "glm\glm.hpp"
#include "glm\gtc\type_ptr.hpp"
#include "glm\gtc\matrix_transform.hpp"
#include "Utils\6.3 ImportedModel.h"
#include "Utils\5.2 Utils.h"
using namespace std;
#define numVAOs 1
#define numVBOs 3
float cameraX, cameraY, cameraZ;
float pyrLocX, pyrLocY, pyrLocZ;
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
GLuint brickTexture; // 纹理图片ID// allocate variables used in display() function, 
// so that they won’t need to be allocated during rendering
GLuint mvLoc, projLoc;
int width, height;
float aspect;
glm::mat4 pMat, vMat, mMat, mvMat;ImportedModel myModel("add/6.3 cube1.obj"); // in top-level declarationsvoid setupVertices(void) {std::vector<glm::vec3> vert = myModel.getVertices();std::vector<glm::vec2> tex = myModel.getTextureCoords();std::vector<glm::vec3> norm = myModel.getNormals();int numObjVertices = myModel.getNumVertices(); // 出错,这一行的值竟然是0std::vector<float> pvalues; // vertex positionsstd::vector<float> tvalues; // texture coordinatesstd::vector<float> nvalues; // normal vectorsfor (int i = 0; i < numObjVertices; i++) {pvalues.push_back((vert[i]).x);pvalues.push_back((vert[i]).y);pvalues.push_back((vert[i]).z);tvalues.push_back((tex[i]).s);tvalues.push_back((tex[i]).t);nvalues.push_back((norm[i]).x);nvalues.push_back((norm[i]).y);nvalues.push_back((norm[i]).z);}glGenVertexArrays(1, vao);glBindVertexArray(vao[0]);glGenBuffers(numVBOs, vbo);// VBO for vertex locationsglBindBuffer(GL_ARRAY_BUFFER, vbo[0]);glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);// VBO for texture coordinatesglBindBuffer(GL_ARRAY_BUFFER, vbo[1]);glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);// VBO for normal vectorsglBindBuffer(GL_ARRAY_BUFFER, vbo[2]);glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
}void init(GLFWwindow* window) {renderingProgram = createShaderProgram("add/5.2 vertShader.glsl", "add/5.2 fragShader.glsl");cameraX = -3.0f; cameraY = 4.0f; cameraZ = 15.0f;pyrLocX = 0.0f; pyrLocY = 0.0f; pyrLocZ = 0.0f; // shift down Y to reveal perspectivesetupVertices();brickTexture = loadTexture("add/6.1 earth.jpg"); // 加载纹理的图片
}void display(GLFWwindow* window, double currentTime) {glClear(GL_DEPTH_BUFFER_BIT);glUseProgram(renderingProgram);// get the uniform variables for the MV and projection matricesmvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");// build perspective matrixglfwGetFramebufferSize(window, &width, &height);aspect = (float)width / (float)height;pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees// build view matrix, model matrix, and model-view matrixvMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));// vbo[0]mMat = glm::translate(glm::mat4(1.0f), glm::vec3(pyrLocX, pyrLocY, pyrLocZ));mvMat = vMat * mMat;// copy perspective and MV matrices to corresponding uniform variablesglUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));// associate VBO with the corresponding vertex attribute in the vertex shaderglBindBuffer(GL_ARRAY_BUFFER, vbo[0]);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);glEnableVertexAttribArray(0);// 纹理glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);glEnableVertexAttribArray(1);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, brickTexture);// vb0[2]glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);glEnableVertexAttribArray(2);// adjust OpenGL settings and draw modelglEnable(GL_DEPTH_TEST);glDepthFunc(GL_LEQUAL);glFrontFace(GL_CCW);// 锥体的三角形是逆时针的面认为是正方向glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices());}int main(void) { // main() is unchanged from beforeif (!glfwInit()) { exit(EXIT_FAILURE); }glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);GLFWwindow* window = glfwCreateWindow(1200, 800, "Chapter 6 - program 2", NULL, NULL);glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }glfwSwapInterval(1);init(window);while (!glfwWindowShouldClose(window)) {display(window, glfwGetTime());glfwSwapBuffers(window);glfwPollEvents();}glfwDestroyWindow(window);glfwTerminate();exit(EXIT_SUCCESS);}

② 6.3 ImportedModel.cpp 一个类,用于读取obj文件

#include <fstream>
#include <sstream>
#include <glm\glm.hpp>
#include "6.3 ImportedModel.h"
#include <iostream> // cout 函数需要用
using namespace std;
// ------------ Imported Model class
ImportedModel::ImportedModel(const char *filePath) {ModelImporter modelImporter = ModelImporter();modelImporter.parseOBJ(filePath); // uses modelImporter to get vertex informationnumVertices = modelImporter.getNumVertices(); // 出错,这里的数值还是0std::vector<float> verts = modelImporter.getVertices();std::vector<float> tcs = modelImporter.getTextureCoordinates();std::vector<float> normals = modelImporter.getNormals();for (int i = 0; i < numVertices; i++) {vertices.push_back(glm::vec3(verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2]));texCoords.push_back(glm::vec2(tcs[i * 2], tcs[i * 2 + 1]));normalVecs.push_back(glm::vec3(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]));}}int ImportedModel::getNumVertices() {   return numVertices; } // accessors // 出错,这里的数值还是0
std::vector<glm::vec3> ImportedModel::getVertices() { return vertices; }
std::vector<glm::vec2> ImportedModel::getTextureCoords() { return texCoords; }
std::vector<glm::vec3> ImportedModel::getNormals() { return normalVecs; }
// -------------- Model Importer class
ModelImporter::ModelImporter() {}
void ModelImporter::parseOBJ(const char *filePath) {float x, y, z;string content;ifstream fileStream(filePath, ios::in);string line = "";int iNum = 0;//调试while (!fileStream.eof()) {getline(fileStream, line);if (line.compare(0, 2, "v ") == 0) { // vertex position ("v" case)stringstream ss(line.erase(0, 1));ss >> x; ss >> y; ss >> z; // extract the vertex position valuesvertVals.push_back(x);vertVals.push_back(y);vertVals.push_back(z);}if (line.compare(0, 2, "vt") == 0) { // texture coordinates ("vt" case)stringstream ss(line.erase(0, 2));ss >> x; ss >> y; // extract texture coordinate valuesstVals.push_back(x);stVals.push_back(y);}if (line.compare(0, 2, "vn") == 0) { // vertex normals ("vn" case)stringstream ss(line.erase(0, 2));ss >> x; ss >> y; ss >> z; // extract the normal vector valuesnormVals.push_back(x);normVals.push_back(y);normVals.push_back(z);}if (line.compare(0, 2, "f ") == 0) { // 注意,这里的 f 后面必须加一个空格,因为line.compare(0, 2, "f ")中表示从索引0开始的2个字符,而f不加空格只有一个字符string oneCorner, v, t, n;stringstream ss(line.erase(0, 2));for (int i = 0; i < 3; i++) {getline(ss, oneCorner, ' '); // extract triangle face referencesstringstream oneCornerSS(oneCorner);getline(oneCornerSS, v, '/');getline(oneCornerSS, t, '/');getline(oneCornerSS, n, '/');int vertRef = (stoi(v) - 1) * 3; // "stoi" converts string to integerint tcRef = (stoi(t) - 1) * 2;int normRef = (stoi(n) - 1) * 3;triangleVerts.push_back(vertVals[vertRef]); // build vector of verticestriangleVerts.push_back(vertVals[vertRef + 1]);triangleVerts.push_back(vertVals[vertRef + 2]);textureCoords.push_back(stVals[tcRef]); // build vector of texture coordstextureCoords.push_back(stVals[tcRef + 1]);normals.push_back(normVals[normRef]); //… and normalsnormals.push_back(normVals[normRef + 1]);normals.push_back(normVals[normRef + 2]);}}}
}
int ModelImporter::getNumVertices() { return (triangleVerts.size() / 3); } // accessors  // 先注释,为了调试,等调试结束再解除注释
std::vector<float> ModelImporter::getVertices() { return triangleVerts; }
std::vector<float> ModelImporter::getTextureCoordinates() { return textureCoords; }
std::vector<float> ModelImporter::getNormals() { return normals; }

③ 6.3 ImportedModel.h 类的声明

#pragma once
#include <vector>
class ImportedModel
{
private:int numVertices;std::vector<glm::vec3> vertices;std::vector<glm::vec2> texCoords;std::vector<glm::vec3> normalVecs;
public:ImportedModel(const char *filePath);int getNumVertices();std::vector<glm::vec3> getVertices();std::vector<glm::vec2> getTextureCoords();std::vector<glm::vec3> getNormals();
};
class ModelImporter
{
private:// values as read in from OBJ filestd::vector<float> vertVals;std::vector<float> stVals;std::vector<float> normVals;// values stored for later use as vertex attributesstd::vector<float> triangleVerts;std::vector<float> textureCoords;std::vector<float> normals;
public:ModelImporter();void parseOBJ(const char *filePath);int getNumVertices();std::vector<float> getVertices();std::vector<float> getTextureCoordinates();std::vector<float> getNormals();
};

④ 5.2 Utils.cpp 一些调用函数,比如检测错误的函数

#include "Utils/5.2 Utils.h"
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "SOIL2/SOIL2.h"#include <iostream>
#include <string>
#include <fstream>
using namespace std;GLuint createShaderProgram(const char* a_Path, const char* b_Path) {GLint vertCompiled;GLint fragCompiled;GLint linked;string vertShaderStr = readShaderSource(a_Path); // 文件在add文件夹中string fragShaderStr = readShaderSource(b_Path);const char *vertShaderSrc = vertShaderStr.c_str();const char *fragShaderSrc = fragShaderStr.c_str();GLuint vShader = glCreateShader(GL_VERTEX_SHADER);GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(vShader, 1, &vertShaderSrc, NULL);glShaderSource(fShader, 1, &fragShaderSrc, NULL);// 在编译着色器时,捕捉错误glCompileShader(vShader);checkOpenGLError();glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled);if (vertCompiled != 1) {cout << "vertex compilation failed" << endl;printShaderLog(vShader);}glCompileShader(fShader);checkOpenGLError();glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled);if (fragCompiled != 1) {cout << "fragment compilation failed" << endl;printShaderLog(fShader);}GLuint vfProgram = glCreateProgram();glAttachShader(vfProgram, vShader);glAttachShader(vfProgram, fShader);glLinkProgram(vfProgram);checkOpenGLError();glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);if (linked != 1) {cout << "linking failed" << endl;printProgramLog(vfProgram);}return vfProgram;
}string readShaderSource(const char *filePath) {string content;ifstream fileStream(filePath, ios::in);string line = "";while (!fileStream.eof()) {getline(fileStream, line);content.append(line + "\n");}fileStream.close();return content;
}
bool checkOpenGLError() {bool foundError = false;int glErr = glGetError();while (glErr != GL_NO_ERROR) {cout << "glError: " << glErr << endl;foundError = true;glErr = glGetError();}return foundError;
}void printProgramLog(int prog) {int len = 0;int chWrittn = 0;char *log;glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);if (len > 0) {log = (char *)malloc(len);glGetProgramInfoLog(prog, len, &chWrittn, log);cout << "Program Info Log: " << log << endl;free(log);}
}
void printShaderLog(GLuint shader) {int len = 0;int chWrittn = 0;char *log;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);if (len > 0) {log = (char *)malloc(len);glGetShaderInfoLog(shader, len, &chWrittn, log);cout << "Shader Info Log: " << log << endl;free(log);}
}GLuint loadTexture(const char *texImagePath) {GLuint textureID;textureID = SOIL_load_OGL_texture(texImagePath,SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);if (textureID == 0) cout << "could not find texture file" << texImagePath << endl;// mipmapsglBindTexture(GL_TEXTURE_2D, textureID); 不倒置也不镜像——多选一//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 倒置+镜像——多选一//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); 仅有一个,其余颜色为红色——多选一//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);//float redColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f };//glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, redColor);// 仅有一个,其余为图片最边缘一行一列的颜色拓展——多选一glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glGenerateMipmap(GL_TEXTURE_2D);// if also anisotropic filtering if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {GLfloat anisoSetting = 0.0f;glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoSetting);//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoSetting);}return textureID;
}

⑤ 5.2 Utils.h 调用函数声明

#pragma once
#ifndef UTILS_H
#define UTILS_H
#include <string>
#include "GL\glew.h"
#include "GLFW\glfw3.h"
using namespace std;GLuint createShaderProgram(const char* a_Path, const char* b_Path);
string readShaderSource(const char *filePath);
bool checkOpenGLError();
void printProgramLog(int prog);
void printShaderLog(GLuint shader);
GLuint loadTexture(const char *texImagePath);#endif // !UTILS_H

⑥ 5.2 vertShader.glsl 点着色器,确定3D点坐标

#version 430
layout (location=0) in vec3 pos;
layout (location=1) in vec2 texCoord;
out vec2 tc; // texture coordinate output to rasterizer for interpolation
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp; // not used in vertex shader 
void main(void)
{	 gl_Position = proj_matrix * mv_matrix * vec4(pos,1.0);tc = texCoord;
}

⑦ 5.2 fragShader.glsl 片着色器,确定每个点的颜色

#version 430
in vec2 tc; // interpolated incoming texture coordinate
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;
void main(void)
{ color = texture(samp, tc);
}

⑧ 6.3 cube1.obj obj文件,这里用的是Blender软件生成的

# 以下是记事本的obj内容
# Blender v3.0.1 OBJ File: ''
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

⑨ 6.1 earth.jpg 一张图片

在这里插入图片描述


http://chatgpt.dhexx.cn/article/c2QnGau6.shtml

相关文章

unity动态加载obj文件

unity2018.4.2f1 vs2017 最近项目需求&#xff0c;需要实现动态读物外部obj模型&#xff0c;并加载到场景中&#xff0c;研究了好几天&#xff0c;终于实现了&#xff0c;在此做个记录。 1、首先随便找个.obj模型&#xff0c;带贴图&#xff0c;我的资源截图如下&#xff1a…

obj文件(3): 如何用matlab 打开obj文件

obj文件:如何用 matlab 打开 obj 文件 第一步&#xff1a;检查obj文本格式第二步&#xff1a;用 Blender 软件重新导出obj文件第三步&#xff1a;用excel 打开这个obj 文件第四步&#xff1a;复制excel中的数据&#xff0c;传给matlab第五步&#xff1a; 用matlab 打开3D模型 第…

三维模型obj文件解析

目录 obj文件简介文件结构顶点数据(Vertex data)&#xff1a;自由形态曲线(Free-form curve)/表面属性(surface attributes):元素(Elements):自由形态曲线(Free-form curve)/表面主体陈述(surface body statements):自由形态表面之间的连接(Connectivity between free-form sur…

linux类动态库,Linux动态库(一)

起因 博主在以Linux下做开发。在软件需求中&#xff0c;需要动态库带来的灵活性。 比如说博主主导的智能主机的开发。它需要支持很多种类的设备控制&#xff0c;如普通的开关灯、RGB灯、窗帘、百叶窗等等。我们将这些设备抽象成Device类&#xff0c;具体的设备就从这个类上派生…

OpenFlow Switch

The picture of OpenFlow Switch openflow 架构分为2层&#xff0c;一个是控制器层&#xff0c;一个是switch 层。中间是由openflow protocal进行连接的&#xff0c;负责传输指令与数据。switch分为3大块&#xff0c;第一是openflow channe&#xff0c;他是负责向控制器传输数据…

【博客450】OpenFlow学习

OpenFlow OpenFlow协议规范定义了OpenFlow交换机、流表、OpenFlow通道以及OpenFlow交换协议。 OpenFlow是第一个开放的南向接口协议&#xff0c;也是目前最流行的南向协议。它提出了控制与转发分离的架构&#xff0c;规定了SDN转发设备的基本组件和功能要求&#xff0c;以及与控…

OpenFlow交换机【ACM SIGCOMM顶会论文笔记】

目录 写在前面的话OpenFlow交换机基本思想与工作原理专用OpenFlow交换机&#xff08;Dedicated OpenFlow switches&#xff09; 启用OpenFlow的交换机&#xff08;OpenFlow-enabled switches&#xff09;其他功能&#xff08;Additional features&#xff09;控制器&#xff08…

关于ns-3中安装openflow的问题,解决openflow not found

官网 官网里面并没有明确的描述&#xff0c;这里结合自己的安装过程讲解一下 首先下载源码&#xff0c;记住这一步最好在ns-3目录下&#xff0c;就是运行waf命名的目录 $ hg clone http://code.nsnam.org/jpelkey3/openflow $ cd openflow进行编译&#xff0c;会提示缺少文件…

OpenFlow基础入门知识

本文进行讨论的是OpenFlow 1.0和OpenFlow 1.3的基本知识 Overview&#xff1a; Openflow 1.0&#xff1a; 安全通道单张流表ipv4 Openflow 1.3&#xff1a; 安全通道多级流表&#xff08;流水线pipeline&#xff09;组表测量表ipv6..... 流&#xff08;flow&#xff09; …

Openflow流表学习

Openflow流表学习 OpenFlow是一种新型的网络协议&#xff0c;它是控制器和交换机之间的标准协议。自2009年底发布1.0版本后&#xff0c;OpenFlow协议又经历了1.1、1.2、1.3及1.4版本的演进过程&#xff0c;目前使用和支持最多的是1.0和1.3版本。 OpenFlow1.3在1.0版的基础上进…

SDN与OPENFLOW 简介

本文对Openflow的发展、规范、应用和SDN的提出及相关应用做出较为客观全面的介绍。笔者希望通过本文对OpenFlow/SDN做一个初步介绍&#xff0c;以期帮助大家能够进一步深入了解和学习OpenFlow/SDN。 序言&#xff1a;从网络虚拟化说起 云计算的发展&#xff0c;是以虚拟化技术…

OpenFlow协议分析

实验环境&#xff1a;CentOS OpenDayLight-Carbon mininet WireShark 本实验通过wireshark抓包分析openflow1.3协议的各种报文与字段。 抓包 首先安装好实验所需的软件&#xff0c;这里不多赘述&#xff0c;需要的可以点击查看&#xff1a;mininet多方法安装&#xff0c;控制…

openflow简介

openflow交换机包含一些流表&#xff0c;流表负责具体包查找和转发 控制器通过of协议对流表查询和管理 一、流表 流表组成&#xff1a; 包头域、活动计数器、0个或多个执行行动 包头域&#xff1a; 计数器&#xff1a; 可以针对每张表、每个流、每个端口、每个队列来维护。…

SDN-OpenFlow1.0协议分析

目录 OpenFlow1.0代码 OpenFlow交换机流表 包头域 计数器 行动 流表匹配 OpenFlow消息 OpenFlow消息格式 对称消息 建立OpenFlow连接&#xff08;OFPT_HELLO消息&#xff09; 报告错误&#xff08;OFPT_ERROR消息&#xff09; 获取交换机特性信息&#xff08;Featu…

OpenFlow概述

OpenFlow简介 通俗的讲&#xff0c;OpenFlow是使用类似于API进程配置网络交换机的协议。OpenFlow的思路很简单&#xff0c;网络设备维护一个FlowTable并且只按照FlowTable进行转发&#xff0c;FlowTable本身的生成、维护、下发完全由外置的Controller来实现&#xff0c;注意这里…

OpenFlow了解

openflow的核心思想是将所有的协议都抽象出来&#xff0c;抽象成公共的flow概念。协议抽象&#xff1a;数据&#xff0c;函数&#xff08;对数据的处理方式&#xff09;&#xff0c;逻辑&#xff08;数据与处理的对应关系&#xff0c;函数之间的交互行为与时序&#xff09; pu…

OpenFlow交换机概述

1 交换机组成 OpenFlow交换机包括一个或多个流表和一个组表&#xff0c;执行分组查找和转发&#xff0c;和到一个外部控制器OpenFlow的信道。 控制器使用OpenFlow的协议&#xff0c;可添加、更新和删除流表中表项&#xff0c;既主动或被动响应数据包。 每个流表项包含匹配字段&…

openflow阅读感悟

一、背景 随着网络的快速发展和普及&#xff0c;设备和协议的复杂性导致了网络实验的困难。当时&#xff0c;几乎没有实际方法可以在足够现实的环境中尝试新的网络协议&#xff0c;来自网络学术、产业界的大多数新想法都未经试用和测试。因此&#xff0c;人们普遍认为网络基础设…

OpenFlow概念

OpenFlow是一种网络通信协议&#xff0c;应用于SDN架构中控制器和转发器之间的通信。软件定义网络SDN的一个核心思想就是“转发、控制分离”&#xff0c;要实现转、控分离&#xff0c;就需要在控制器与转发器之间建立一个通信接口标准&#xff0c;允许控制器直接访问和控制转发…

OpenFlow总结

OpenFlow总结 OpenFlow体系结构OpenFlow端口1.1、物理端口1.2、逻辑端口1.3、预定端口&#xff08;OpenFlow1.5中文版&#xff09; OpenFlow流表&#xff08;FlowTable&#xff09;2.1、概念2.2、流表结构 OpenFlow通信通道3.1、消息类型3.2 、消息交换 OpenFlow体系结构 Open…