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

  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

结果

生成:

C++/OpenGL 入门(18):读取obj文件并贴图_第1张图片

读取:

C++/OpenGL 入门(18):读取obj文件并贴图_第2张图片
C++/OpenGL 入门(18):读取obj文件并贴图_第3张图片

笔记

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文件
    C++/OpenGL 入门(18):读取obj文件并贴图_第4张图片
  • 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,即可下载。

C++/OpenGL 入门(18):读取obj文件并贴图_第5张图片
软件界面中有新手教程自带正方体,右上角“文件”->“导出”->“obj”
C++/OpenGL 入门(18):读取obj文件并贴图_第6张图片
C++/OpenGL 入门(18):读取obj文件并贴图_第7张图片
在obj导出弹窗,选择存储位置和存储名称,选择包括在内的内容,包括顶点,法线和三角面
C++/OpenGL 入门(18):读取obj文件并贴图_第8张图片
然后在相应位置会生成obj文件,可以右键选择用记事本打开
C++/OpenGL 入门(18):读取obj文件并贴图_第9张图片

调试中的错误与解决办法

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

    C++/OpenGL 入门(18):读取obj文件并贴图_第13张图片

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

  • 关于stringstream 的应用参考: c++ stringstream ss()_wesigj的博客-CSDN博客_istringstream ss(str)
    C++/OpenGL 入门(18):读取obj文件并贴图_第14张图片
    找到错误原因了:
    Line.compare(0, 2, “f ”); 表示,从索引0开始的2个字符进行对比,所以f 后面一定要加一个空格,构成2个字符,
    C++/OpenGL 入门(18):读取obj文件并贴图_第15张图片

完整代码

一共需要 个文件,分别是:
① 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 
#include 
#include 
#include 


#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 declarations




void 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(); // 出错,这一行的值竟然是0
	std::vector<float> pvalues; // vertex positions
	std::vector<float> tvalues; // texture coordinates
	std::vector<float> nvalues; // normal vectors
	
	for (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 locations
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
	// VBO for texture coordinates
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);
	// VBO for normal vectors
	glBindBuffer(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 perspective
	
	setupVertices();
	
	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 matrices
	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
	// build perspective matrix
	glfwGetFramebufferSize(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 matrix
	vMat = 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 variables
	glUniformMatrix4fv(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 shader
	glBindBuffer(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 model
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glFrontFace(GL_CCW);// 锥体的三角形是逆时针的面认为是正方向

	glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices());

}

int main(void) { // main() is unchanged from before

	

	if (!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 
#include 
#include 
#include "6.3 ImportedModel.h"
#include  // cout 函数需要用
using namespace std;
// ------------ Imported Model class
ImportedModel::ImportedModel(const char *filePath) {
	ModelImporter modelImporter = ModelImporter();
	modelImporter.parseOBJ(filePath); // uses modelImporter to get vertex information
	numVertices = modelImporter.getNumVertices(); // 出错,这里的数值还是0
	std::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 values
			
			vertVals.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 values
			stVals.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 values
			normVals.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 references
				stringstream oneCornerSS(oneCorner);
				getline(oneCornerSS, v, '/');
				getline(oneCornerSS, t, '/');
				getline(oneCornerSS, n, '/');
				int vertRef = (stoi(v) - 1) * 3; // "stoi" converts string to integer
				int tcRef = (stoi(t) - 1) * 2;
				int normRef = (stoi(n) - 1) * 3;
				triangleVerts.push_back(vertVals[vertRef]); // build vector of vertices
				triangleVerts.push_back(vertVals[vertRef + 1]);
				triangleVerts.push_back(vertVals[vertRef + 2]);
				textureCoords.push_back(stVals[tcRef]); // build vector of texture coords
				textureCoords.push_back(stVals[tcRef + 1]);
				normals.push_back(normVals[normRef]); //… and normals
				normals.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 
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 file
	std::vector<float> vertVals;
	std::vector<float> stVals;
	std::vector<float> normVals;
	// values stored for later use as vertex attributes
	std::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 
#include 
#include 
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;
	
	// mipmaps
	glBindTexture(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 
#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 一张图片

C++/OpenGL 入门(18):读取obj文件并贴图_第16张图片

你可能感兴趣的:(OpenGL学习,c++)